﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-兔子的技术博客-随笔分类-系统API，底层技术</title><link>http://www.cppblog.com/flyinghare/category/11479.html</link><description>兔子</description><language>zh-cn</language><lastBuildDate>Wed, 16 Oct 2013 12:09:14 GMT</lastBuildDate><pubDate>Wed, 16 Oct 2013 12:09:14 GMT</pubDate><ttl>60</ttl><item><title>批处理比较数值大小 lss，equ和gtr的用法</title><link>http://www.cppblog.com/flyinghare/archive/2013/10/16/203775.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 16 Oct 2013 11:46:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2013/10/16/203775.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/203775.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2013/10/16/203775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/203775.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/203775.html</trackback:ping><description><![CDATA[<div style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">批处理比较数值大小 lss，equ和gtr的用法<p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px; line-height: 20px;"><span style="word-wrap: break-word;"><a href="http://panfutian.blog.163.com/blog/#m=0&amp;t=1&amp;c=fks_084067084086088070084083081095085082084071092095084067" rel="nofollow" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #19599b;">&#9733;电脑综合&#9733;</a>&nbsp;<span style="word-wrap: break-word;">2010-04-17 14:18:39</span>&nbsp;<span style="word-wrap: break-word;">阅读<span style="word-wrap: break-word;">196</span></span>&nbsp;<span style="word-wrap: break-word;">评论<span style="word-wrap: break-word;">0</span></span>&nbsp;</span><span style="word-wrap: break-word;">&nbsp;&nbsp; 字号：<span style="word-wrap: break-word;"><u style="word-wrap: break-word;">大</u></span><span style="word-wrap: break-word;"><strong style="word-wrap: break-word;">中</strong></span><span style="word-wrap: break-word;"><u style="word-wrap: break-word;">小</u></span></span><span style="word-wrap: break-word;">&nbsp;<a rel="nofollow" style="word-wrap: break-word; color: #19599b;">订阅</a></span></p><div style="word-wrap: break-word;"><div style="word-wrap: break-word;"><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp;&nbsp; EQU - 等于<br style="word-wrap: break-word;" />NEQ - 不等于<br style="word-wrap: break-word;" />LSS - 小于<br style="word-wrap: break-word;" />LEQ - 小于或等于<br style="word-wrap: break-word;" />GTR - 大于<br style="word-wrap: break-word;" />GEQ - 大于或等于</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">比较大小.bat的源程序如下：<br style="word-wrap: break-word;" /><br style="word-wrap: break-word;" />@echo off<br style="word-wrap: break-word;" />set /p 第一个数=请输入第一个数- c% F. C2 k: ~# R<br style="word-wrap: break-word;" />set /p 第二个数=请输入第二个数<br style="word-wrap: break-word;" />if %第二个数% lss %第一个数% goto hero7 |! U; ~8 ?5 ?7 p9 g6 v. Y3 w" K0 v<br style="word-wrap: break-word;" />if %第二个数% equ %第一个数% goto her<br style="word-wrap: break-word;" />if %第二个数% gtr %第一个数% goto he9 @! i8 q&amp; c; j; Z/ Q% ]<br style="word-wrap: break-word;" />pause2 W) H7 w/ R$ @&nbsp;&nbsp; g8 P<br style="word-wrap: break-word;" />exit<br style="word-wrap: break-word;" />:hero&amp; b8 K1 _5 C' U0 A/ l% D<br style="word-wrap: break-word;" />echo 第一个数比第二个数大！<br style="word-wrap: break-word;" />pause# A6 \8 X9 P) t- B9 Z- g2 e1 a<br style="word-wrap: break-word;" />%0<br style="word-wrap: break-word;" />:her' t) K1 j. h# L<br style="word-wrap: break-word;" />echo 第一个数等于第二个数！<br style="word-wrap: break-word;" />pause<br style="word-wrap: break-word;" />%0<br style="word-wrap: break-word;" />:he- o$ e* U# ]" }4 |! V% ^" c<br style="word-wrap: break-word;" />echo 第一个数比第二个数小！<br style="word-wrap: break-word;" />pause<br style="word-wrap: break-word;" />%07 \! s- e$ [0 E6 @<br style="word-wrap: break-word;" />: T+ d6 Y# o0 [" H4 P% C<br style="word-wrap: break-word;" /><br style="word-wrap: break-word;" />比较大小另 版.bat 的源程序如下：<br style="word-wrap: break-word;" />5 B. b6 H8 i/ D* L<br style="word-wrap: break-word;" />@echo off; R% @1 ~% g' W<br style="word-wrap: break-word;" />set /p 第一个数=请输入第一个数<br style="word-wrap: break-word;" />set /p 第二个数=请输入第二个数<br style="word-wrap: break-word;" />if %第二个数% lss %第一个数% goto hero<br style="word-wrap: break-word;" />if %第二个数% equ %第一个数% goto her' N( d7 j&nbsp;&nbsp; i$ r8 e" A<br style="word-wrap: break-word;" />if %第二个数% gtr %第一个数% goto he" h2 ~4 P2 m' V<br style="word-wrap: break-word;" />:hero0 h( k, ?) \( C" t, \<br style="word-wrap: break-word;" />echo 第一个数比第二个数大！5 a4 i$ L7 p% M; e% ]! T<br style="word-wrap: break-word;" />pause<br style="word-wrap: break-word;" />goto end<br style="word-wrap: break-word;" />:her&amp; ~2 r' Q7 X! g: j0 I&amp; x<br style="word-wrap: break-word;" />echo 第一个数等于第二个数！<br style="word-wrap: break-word;" />pause$ k4 |( v4 B: _$ `" ~<br style="word-wrap: break-word;" />goto end<br style="word-wrap: break-word;" />:he&nbsp;&nbsp; ^3 U/ K* S" W- D7 o: t<br style="word-wrap: break-word;" />echo 第一个数比第二个数小！<br style="word-wrap: break-word;" />pause<br style="word-wrap: break-word;" />goto end<br style="word-wrap: break-word;" />:end. t; R( v+ H# l4 C0 l( f( _. q# i<br style="word-wrap: break-word;" />exit<br style="word-wrap: break-word;" /><br style="word-wrap: break-word;" /><br style="word-wrap: break-word;" /><br style="word-wrap: break-word;" /><br style="word-wrap: break-word;" /><br style="word-wrap: break-word;" /><strong style="word-wrap: break-word;"><span style="word-wrap: break-word;">命令行下如何用批处理比较数据大小？</span></strong></p><div style="word-wrap: break-word;"><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;"><strong style="word-wrap: break-word;"><br style="word-wrap: break-word;" />set var1=3<br style="word-wrap: break-word;" />set var2=2<br style="word-wrap: break-word;" />if %var1% GTR %var2% goto ...<br style="word-wrap: break-word;" /></strong><br style="word-wrap: break-word;" /><strong style="word-wrap: break-word;">if /?:</strong><br style="word-wrap: break-word;" />执行批处理程序中的条件处理。<br style="word-wrap: break-word;" />IF [NOT] ERRORLEVEL number command<br style="word-wrap: break-word;" />IF [NOT] string1==string2 command<br style="word-wrap: break-word;" />IF [NOT] EXIST filename command<br style="word-wrap: break-word;" />NOT &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 指定只有条件为 false 的情况下，Windows 才<br style="word-wrap: break-word;" />应该执行该命令。<br style="word-wrap: break-word;" />ERRORLEVEL number 如果最后运行的程序返回一个等于或大于<br style="word-wrap: break-word;" />指定数字的退出编码，指定条件为 true。<br style="word-wrap: break-word;" />string1==string2&nbsp;&nbsp; 如果指定的文字字符串匹配，指定条件为 true。<br style="word-wrap: break-word;" />EXIST filename 如果指定的文件名存在，指定条件为 true。<br style="word-wrap: break-word;" />command &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 如果符合条件，指定要执行的命令。如果指定的<br style="word-wrap: break-word;" />条件为 FALSE，命令后可跟一个执行 ELSE&nbsp;<br style="word-wrap: break-word;" />关键字后的命令的 ELSE 命令。<br style="word-wrap: break-word;" />ELSE 子句必须在 IF 之后出现在同一行上。例如:<br style="word-wrap: break-word;" />IF EXIST filename. (<br style="word-wrap: break-word;" />del filename.<br style="word-wrap: break-word;" />) ELSE (<br style="word-wrap: break-word;" />echo filename. missing.<br style="word-wrap: break-word;" />)<br style="word-wrap: break-word;" />因为 del 命令需要用一个新行终止，以下子句不会有效:<br style="word-wrap: break-word;" />IF EXIST filename. del filename. ELSE echo filename. missing<br style="word-wrap: break-word;" />由于 ELSE 命令必须与 IF 命令的尾端在同一行上，以下子句也<br style="word-wrap: break-word;" />不会有效:<br style="word-wrap: break-word;" />IF EXIST filename. del filename.<br style="word-wrap: break-word;" />ELSE echo filename. missing<br style="word-wrap: break-word;" />如果都放在同一行上，以下子句有效:<br style="word-wrap: break-word;" />IF EXIST filename. (del filename.) ELSE echo filename. missing<br style="word-wrap: break-word;" />如果命令扩展被启用，IF 会如下改变:<br style="word-wrap: break-word;" />IF [/I] string1 compare-op string2 command<br style="word-wrap: break-word;" />IF CMDEXTVERSION number command<br style="word-wrap: break-word;" />IF DEFINED variable command<br style="word-wrap: break-word;" />其中， compare-op 可以是:<br style="word-wrap: break-word;" />EQU - 等于<br style="word-wrap: break-word;" />NEQ - 不等于<br style="word-wrap: break-word;" />LSS - 小于<br style="word-wrap: break-word;" />LEQ - 小于或等于<br style="word-wrap: break-word;" />GTR - 大于<br style="word-wrap: break-word;" />GEQ - 大于或等于<br style="word-wrap: break-word;" />而 /I 开关(如果指定)说明要进行的字符串比较不分大小写。<br style="word-wrap: break-word;" />/I 开关可以用于 IF 的 string1==string2 的形式上。这些<br style="word-wrap: break-word;" />比较都是通用的；原因是，如果 string1 和 string2 都是<br style="word-wrap: break-word;" />由数字组成的，字符串会被转换成数字，进行数字比较。<br style="word-wrap: break-word;" />CMDEXTVERSION 条件的作用跟 ERRORLEVEL 的一样，除了它<br style="word-wrap: break-word;" />是在跟与命令扩展有关联的内部版本号比较。第一个版本<br style="word-wrap: break-word;" />是 1。每次对命令扩展有相当大的增强时，版本号会增加一个。<br style="word-wrap: break-word;" />命令扩展被停用时，CMDEXTVERSION 条件不是真的。<br style="word-wrap: break-word;" />如果已定义环境变量，DEFINED 条件的作用跟 EXISTS 的一样，<br style="word-wrap: break-word;" />除了它取得一个环境变量，返回的结果是 true。<br style="word-wrap: break-word;" />如果没有名为 ERRORLEVEL 的环境变量，%ERRORLEVEL%<br style="word-wrap: break-word;" />会扩充为 ERROLEVEL 当前数值的字符串表达式；否则，您会得到<br style="word-wrap: break-word;" />其数值。运行程序后，以下语句说明 ERRORLEVEL 的用法:<br style="word-wrap: break-word;" />goto answer%ERRORLEVEL%<br style="word-wrap: break-word;" />:answer0<br style="word-wrap: break-word;" />echo Program had return code 0<br style="word-wrap: break-word;" />:answer1<br style="word-wrap: break-word;" />echo Program had return code 1<br style="word-wrap: break-word;" />您也可以使用以上的数字比较:<br style="word-wrap: break-word;" />IF %ERRORLEVEL% LEQ 1 goto okay<br style="word-wrap: break-word;" />如果没有名为 CMDCMDLINE 的环境变量，%CMDCMDLINE%<br style="word-wrap: break-word;" />将在 CMD.EXE 进行任何处理前扩充为传递给 CMD.EXE 的原始<br style="word-wrap: break-word;" />命令行；否则，您会得到其数值。<br style="word-wrap: break-word;" />如果没有名为 CMDEXTVERSION 的环境变量，<br style="word-wrap: break-word;" />%CMDEXTVERSION% 会扩充为 CMDEXTVERSION 当前数值的<br style="word-wrap: break-word;" />字串符表达式；否则，您会得到其数值。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;"><strong style="word-wrap: break-word;">IF</strong>&nbsp;(DOS命令)</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　执行<a href="http://cache.baidu.com/view/80110.htm" rel="nofollow" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #19599b;">批处理</a>程序中的条件处理。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;[NOT] ERRORLEVEL number command</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;[NOT] string1==string2 command</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;[NOT] EXIST filename command</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　NOT 指定只有条件为 false 的情况下， Windows XP 才</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　应该执行该命令。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　ERRORLEVEL number 如果最后运行的程序返回一个等于或大于</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　指定数字的退出编码，指定条件为 true。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　string1==string2 如果指定的文字字符串匹配，指定条件为 true。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　EXIST filename 如果指定的文件名存在，指定条件为 true。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　command 如果符合条件，指定要执行的命令。如果指定的</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　条件为 FALSE，命令后可跟一个执行 ELSE</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　关键字后的命令的 ELSE 命令。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　ELSE 子句必须在&nbsp;<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;之后出现在同一行上。例如:</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;EXIST filename. (</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　del filename.</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　) ELSE (</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　echo filename. missing.</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　)</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　因为 del 命令需要用一个新行终止，以下子句不会有效:</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;EXIST filename. del filename. ELSE echo filename. missing</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　由于 ELSE 命令必须与&nbsp;<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;命令的尾端在同一行上，以下子句也</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　不会有效:</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;EXIST filename. del filename.</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　ELSE echo filename. missing</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　如果都放在同一行上，以下子句有效:</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;EXIST filename. (del filename.) ELSE echo filename. missing</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　如果命令扩展名被启用，<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;会如下改变:</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;string1 compare-op string2 command</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;CMDEXTVERSION number command</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;DEFINED variable command</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　其中，比较运算符可以是:</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　EQU - 等于</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　NEQ - 不等于</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　LSS - 小于</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　LEQ - 小于或等于</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<a name="baidusnap2" rel="nofollow" style="word-wrap: break-word; color: rgb(25, 89, 155);"></a><strong style="word-wrap: break-word; background-color: #99ff99; color: black;">GTR</strong>&nbsp;- 大于</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　GEQ - 大于或等于</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　及 /I 开关；如果该开关被指定，则说明要进行的字符串比较不分</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　大小写。/I 开关可以用于&nbsp;<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;的 string1==string2 的形式上。这些</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　比较都是通用的；原因是，如果 string1 和 string2 都是由数字</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　组成的，字符串会被转换成数字，进行数字比较。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　CMDEXTVERSION 条件的作用跟 ERRORLEVEL 的一样，除了它</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　是在跟与命令扩展名有关联的内部版本号比较。第一个版本</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　是 1。每次对命令扩展名有相当大的增强时，版本号会增加一个。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　命令扩展名被停用时，CMDEXTVERSION 条件不是真的。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　如果已定义环境变量，DEFINED 条件的作用跟 EXISTS 的一样，</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　除了它取得一个环境变量，返回的结果是 true。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　如果没有名为 ERRORLEVEL 的环境变量，%ERRORLEVEL%</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　会扩充为 ERROLEVEL 当前数值的字符串表达式；否则，您会得到</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　其数值。运行程序后，以下语句说明 ERRORLEVEL 的用法:</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　goto answer%ERRORLEVEL%</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　:answer0</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　echo Program had return code 0</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　:answer1</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　echo Program had return code 1</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　您也可以使用以上的数字比较:</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　<strong style="word-wrap: break-word; background-color: #ffff66; color: black;">IF</strong>&nbsp;%ERRORLEVEL% LEQ 1 goto okay</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　如果没有名为 CMDCMDLINE 的环境变量，%CMDCMDLINE%</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　将在 CMD.EXE 进行任何处理前扩充为传递给 CMD.EXE 的原始</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　命令行；否则，您会得到其数值。</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　如果没有名为 CMDEXTVERSION 的环境变量，</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　%CMDEXTVERSION% 会扩充为 CMDEXTVERSION 当前数值的</p><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">　　字串符表达式；否则，您会得到其数值。</p></div></div><p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">if中gtr的特殊应用(时间&amp;字母对比命令)</p></div></div><div style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">2009年05月06日 星期三 16:49</div><span style="color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">if中gtr的特殊应用作者：youxi01</span><br style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;" /><span style="color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">转载请注明</span><br style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;" /><br style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;" /><span style="color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">一、直接对日期大小进行比较。</span><br style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;" /><span style="color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">比如，我要查找当前目录下，在2005-5-30以后的文件，则可以这样写：</span><div style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;"><span style="word-wrap: break-word;">复制内容到剪贴板</span>&nbsp;代码:<p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">@echo off<br style="word-wrap: break-word;" />for %%i in (*) do if %%~ti gtr 2005-05-30 echo %%~nxi<br style="word-wrap: break-word;" />pause&gt;nul</p></div><span style="color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">二、对字母直接进行比较。</span><div style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;"><span style="word-wrap: break-word;">复制内容到剪贴板</span>&nbsp;代码:<p style="word-wrap: break-word; margin-right: 0px; margin-left: 0px; padding: 0px;">@echo off<br style="word-wrap: break-word;" />call :echo c o<br style="word-wrap: break-word;" />echo.<br style="word-wrap: break-word;" />call :echo d k<br style="word-wrap: break-word;" />pause&gt;nul<br style="word-wrap: break-word;" />:echo startw endw<br style="word-wrap: break-word;" />echo %1 到 %2 之间的字母有：<br style="word-wrap: break-word;" />for %%i in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do (<br style="word-wrap: break-word;" />if %%i geq %1 if %%i leq %2 set /p=%%i &lt;nul)</p></div><span style="color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">三、更奇妙的是，汉字和英文字也可以比较&#8220;大小&#8221;---找出字符串中的汉字</span><br style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;" /><span style="color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">测试代码：&nbsp;</span><span style="word-wrap: break-word; color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">复制内容到剪贴板</span><span style="color: #666666; font-family: 宋体, Arial; font-size: 12px; line-height: 26px; background-color: #ffffff;">&nbsp;代码:</span>@echo off<br style="word-wrap: break-word;" />setlocal enabledelayedexpansion<br style="word-wrap: break-word;" />set "str=我bat是home好.cn人"<br style="word-wrap: break-word;" />call :test "%str%"<br style="word-wrap: break-word;" />echo 提取出的汉字有：%cstr%<br style="word-wrap: break-word;" />pause&gt;nul<br style="word-wrap: break-word;" />:test<br style="word-wrap: break-word;" />set "var=%~1"<br style="word-wrap: break-word;" />for /l %%i in (0 1 20) do (<br style="word-wrap: break-word;" />set "var_=!var:~%%i,1!"<br style="word-wrap: break-word;" />if "!var_!"=="" goto :eof<br style="word-wrap: break-word;" />if !var_! gtr Z set cstr=!cstr!!var_!<br /><br /><br />转自：<a href="http://blog.chinaunix.net/uid-78707-id-3473907.html">http://blog.chinaunix.net/uid-78707-id-3473907.html</a><img src ="http://www.cppblog.com/flyinghare/aggbug/203775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2013-10-16 19:46 <a href="http://www.cppblog.com/flyinghare/archive/2013/10/16/203775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++编写Windows服务程序  </title><link>http://www.cppblog.com/flyinghare/archive/2013/09/26/203443.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Thu, 26 Sep 2013 07:24:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2013/09/26/203443.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/203443.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2013/09/26/203443.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/203443.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/203443.html</trackback:ping><description><![CDATA[<span style="color: #666666; font-family: Arial, Helvetica, simsun, u5b8bu4f53; line-height: 23px; text-indent: 28px; background-color: #ffffff;">环境&nbsp;VC6.0<br /><br /></span><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->#include&nbsp;"windows.h"<br /><br />SERVICE_STATUS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatus;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">服务状态</span><span style="color: #008000; "><br /></span><br />SERVICE_STATUS_HANDLE&nbsp;&nbsp;&nbsp;gSvcStatusHandle;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">服务状态句柄</span><span style="color: #008000; "><br /></span><br />HANDLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ghSvcStopEvent&nbsp;=&nbsp;NULL;<span style="color: #008000; ">//</span><span style="color: #008000; ">服务停止句柄</span><span style="color: #008000; "><br /></span><br /><span style="color: #0000FF; ">#define</span>&nbsp;SERVER_NAME&nbsp;&nbsp;&nbsp;&nbsp;TEXT("my_server")&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">服务名</span><br /><br />VOID&nbsp;WINAPI&nbsp;<span style="color: #333333; font-family: verdana, sans-serif; line-height: 19px;">Server_main</span>(&nbsp;DWORD,&nbsp;LPTSTR&nbsp;*);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">服务入口点</span><span style="color: #008000; "><br /></span><br /><span style="color: #0000FF; ">void</span>&nbsp;ServerReportEvent(LPTSTR&nbsp;szName,LPTSTR&nbsp;szFunction);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">写Windows日志</span><span style="color: #008000; "><br /></span><br />VOID&nbsp;ReportSvcStatus(&nbsp;DWORD,&nbsp;DWORD,&nbsp;DWORD&nbsp;);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">服务状态和SCM交互</span><span style="color: #008000; "><br /></span><br />VOID&nbsp;WINAPI&nbsp;SvcCtrlHandler(&nbsp;DWORD&nbsp;);&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">SCM回调函数</span><span style="color: #008000; "><br /></span><br />VOID&nbsp;ServerProgram(DWORD,&nbsp;LPTSTR&nbsp;*);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">服务主程序</span><span style="color: #008000; "><br /></span><br /><span style="color: #0000FF; ">void</span>&nbsp;main()<br /><br />{&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;SERVICE_TABLE_ENTRY&nbsp;DispatchTable[]&nbsp;=<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;SERVER_NAME,&nbsp;(LPSERVICE_MAIN_FUNCTION)Server_main&nbsp;},<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;NULL,&nbsp;NULL&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(!StartServiceCtrlDispatcher(DispatchTable))<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ServerReportEvent(SERVER_NAME,TEXT("StartServiceCtrlDispatcher"));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />}<br /><br /><span style="color: #0000FF; ">static</span>&nbsp;VOID&nbsp;WINAPI&nbsp;Server_main(DWORD&nbsp;dwArgc,&nbsp;LPTSTR&nbsp;*lpszArgv&nbsp;)<br /><br />{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Register&nbsp;the&nbsp;handler&nbsp;function&nbsp;for&nbsp;the&nbsp;service</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatusHandle&nbsp;=&nbsp;RegisterServiceCtrlHandler(&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SERVER_NAME,&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SvcCtrlHandler);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(&nbsp;!gSvcStatusHandle&nbsp;)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ServerReportEvent(SERVER_NAME,TEXT("RegisterServiceCtrlHandler"));&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;These&nbsp;SERVICE_STATUS&nbsp;members&nbsp;remain&nbsp;as&nbsp;set&nbsp;here</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatus.dwServiceType&nbsp;=&nbsp;SERVICE_WIN32_OWN_PROCESS;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">只有一个单独的服务</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatus.dwServiceSpecificExitCode&nbsp;=&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Report&nbsp;initial&nbsp;status&nbsp;to&nbsp;the&nbsp;SCM</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;ReportSvcStatus(&nbsp;SERVICE_START_PENDING,&nbsp;NO_ERROR,&nbsp;3000&nbsp;);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Perform&nbsp;service-specific&nbsp;initialization&nbsp;and&nbsp;work.</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;ghSvcStopEvent&nbsp;=&nbsp;CreateEvent(<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL,&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;default&nbsp;security&nbsp;attributes</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TRUE,&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;manual&nbsp;reset&nbsp;event</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FALSE,&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;not&nbsp;signaled</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL);&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;no&nbsp;name</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(&nbsp;ghSvcStopEvent&nbsp;==&nbsp;NULL)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReportSvcStatus(&nbsp;SERVICE_STOPPED,&nbsp;NO_ERROR,&nbsp;0&nbsp;);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Report&nbsp;running&nbsp;status&nbsp;when&nbsp;initialization&nbsp;is&nbsp;complete.</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;ReportSvcStatus(&nbsp;SERVICE_RUNNING,&nbsp;NO_ERROR,&nbsp;0&nbsp;);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;TO_DO:&nbsp;Perform&nbsp;work&nbsp;until&nbsp;service&nbsp;stops.&nbsp;</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;ServerProgram(dwArgc,&nbsp;lpszArgv);&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">你需要的操作在此添加代码</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">while</span>(1)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Check&nbsp;whether&nbsp;to&nbsp;stop&nbsp;the&nbsp;service.</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WaitForSingleObject(ghSvcStopEvent,&nbsp;INFINITE);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReportSvcStatus(&nbsp;SERVICE_STOPPED,&nbsp;NO_ERROR,&nbsp;0&nbsp;);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br /><br />}<br /><br /><span style="color: #0000FF; ">void</span>&nbsp;ServerReportEvent(LPTSTR&nbsp;szName,LPTSTR&nbsp;szFunction)&nbsp;<br /><br />{&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;HANDLE&nbsp;hEventSource;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;LPCTSTR&nbsp;lpszStrings[2];<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;len&nbsp;=&nbsp;<span style="color: #0000FF; ">sizeof</span>(szFunction);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;TCHAR&nbsp;*Buffer&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;TCHAR[len];<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;hEventSource&nbsp;=&nbsp;RegisterEventSource(NULL,&nbsp;szName);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(&nbsp;NULL&nbsp;!=&nbsp;hEventSource&nbsp;)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">StringCchPrintf(Buffer,&nbsp;80,&nbsp;TEXT("%s&nbsp;failed&nbsp;with&nbsp;%d"),&nbsp;szFunction,&nbsp;GetLastError());</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(Buffer,szFunction);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpszStrings[0]&nbsp;=&nbsp;szName;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpszStrings[1]&nbsp;=&nbsp;Buffer;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReportEvent(hEventSource,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;event&nbsp;log&nbsp;handle</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EVENTLOG_ERROR_TYPE,&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;event&nbsp;type</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;event&nbsp;category</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SVC_ERROR,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;event&nbsp;identifier</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;no&nbsp;security&nbsp;identifier</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;size&nbsp;of&nbsp;lpszStrings&nbsp;array</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;no&nbsp;binary&nbsp;data</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpszStrings,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;array&nbsp;of&nbsp;strings</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;no&nbsp;binary&nbsp;data&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DeregisterEventSource(hEventSource);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />}<br /><br />VOID&nbsp;ReportSvcStatus(&nbsp;DWORD&nbsp;dwCurrentState,<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;dwWin32ExitCode,<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;dwWaitHint)<br /><br />{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;DWORD&nbsp;dwCheckPoint&nbsp;=&nbsp;1;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Fill&nbsp;in&nbsp;the&nbsp;SERVICE_STATUS&nbsp;structure.</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatus.dwCurrentState&nbsp;=&nbsp;dwCurrentState;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatus.dwWin32ExitCode&nbsp;=&nbsp;dwWin32ExitCode;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatus.dwWaitHint&nbsp;=&nbsp;dwWaitHint;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(dwCurrentState&nbsp;==&nbsp;SERVICE_START_PENDING)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatus.dwControlsAccepted&nbsp;=&nbsp;0;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;gSvcStatus.dwControlsAccepted&nbsp;=&nbsp;SERVICE_ACCEPT_STOP;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(&nbsp;(dwCurrentState&nbsp;==&nbsp;SERVICE_RUNNING)&nbsp;||<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(dwCurrentState&nbsp;==&nbsp;SERVICE_STOPPED)&nbsp;)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gSvcStatus.dwCheckPoint&nbsp;=&nbsp;0;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;gSvcStatus.dwCheckPoint&nbsp;=&nbsp;dwCheckPoint++;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Report&nbsp;the&nbsp;status&nbsp;of&nbsp;the&nbsp;service&nbsp;to&nbsp;the&nbsp;SCM.</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;SetServiceStatus(&nbsp;gSvcStatusHandle,&nbsp;&amp;gSvcStatus&nbsp;);<br /><br />}<br /><br />VOID&nbsp;WINAPI&nbsp;SvcCtrlHandler(&nbsp;DWORD&nbsp;dwCtrl&nbsp;)<br /><br />{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Handle&nbsp;the&nbsp;requested&nbsp;control&nbsp;code.&nbsp;</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">switch</span>(dwCtrl)&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">case</span>&nbsp;SERVICE_CONTROL_STOP:&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReportSvcStatus(SERVICE_STOP_PENDING,&nbsp;NO_ERROR,&nbsp;0);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Signal&nbsp;the&nbsp;service&nbsp;to&nbsp;stop.</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetEvent(ghSvcStopEvent);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">case</span>&nbsp;SERVICE_CONTROL_INTERROGATE:&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Fall&nbsp;through&nbsp;to&nbsp;send&nbsp;current&nbsp;status.</span><span style="color: #008000; "><br /></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">break</span>;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">default</span>:&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">break</span>;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ReportSvcStatus(gSvcStatus.dwCurrentState,&nbsp;NO_ERROR,&nbsp;0);<br /><br />}</div>转自：<a href="http://3140618.blog.163.com/blog/static/745179720109286165959/">http://3140618.blog.163.com/blog/static/745179720109286165959/</a><img src ="http://www.cppblog.com/flyinghare/aggbug/203443.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2013-09-26 15:24 <a href="http://www.cppblog.com/flyinghare/archive/2013/09/26/203443.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP状态转换机说明</title><link>http://www.cppblog.com/flyinghare/archive/2013/06/08/200888.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Sat, 08 Jun 2013 10:09:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2013/06/08/200888.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/200888.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2013/06/08/200888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/200888.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/200888.html</trackback:ping><description><![CDATA[<p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><strong style="line-height: 25px;">建立一个 TCP 连接</strong>TCP 是一个面向连接的协议，无论哪一方向另一方发送数据之前，都必须先在双方之间建立一条连接。本节将详细讨论一个TCP 连接是如何建立的以及通信结束后是如何终止的。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">TCP使用三次握手 （ three-way handshake ） 协议来建立连接，图 3-10 描述了三次握手的报文序列。这三次握手为：</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请求端（通常称为客户）发送一个 SYN 报文段（ SYN 为 1 ）指明客户打算连接的服务器的端口，以及初始顺序号（ ISN ）。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;服务器发回包含服务器的初始顺序号的 SYN 报文段（ SYN 为 1 ）作为应答。同时，将确认号设置为客户的 ISN 加 1 以对客户的 SYN 报文段进行确认（ ACK 也为 1 ）。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 客户必须将确认号设置为服务器的 ISN 加 1 以对服务器的 SYN 报文段进行确认（ ACK 为 1 ），该报文通知目的主机双方已完成连接建立。</p><ul style="line-height: 25px; margin: 5px 0px 5px 40px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;"><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/1490410001683742404.jpg" width="471" height="255" alt="" /><br /></p></ul><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 三次握手协议是连接两端正确同步的充要条件。因为 TCP 建立在不可靠的分组传输服务之上，报文可能丢失、延迟、重复和乱序，因此协议必须使用超时和重传机制。如果重传的连接请求和原先的连接请求在连接正在建立时到达，或者当一个连接已经建立、使用和结束之后，某个延迟的连接请求才到达，就会出现问题。采用三次握手协议（加上这样的规则：在连接建立之后 TCP 就不再理睬又一次的连接请求）就可以解决这些问题。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　三次握手协议可以完成两个重要功能：它确保连接双方做好传输准备，并使双方统一了初始顺序号。初始顺序号是在握手期间传输顺序号并获得确认：当一端为建立连接而发送它的 SYN 时，它为连接选择一个初始顺序号；每个报文段都包括了顺序号字段和确认号字段，这使得两台机器仅仅使用三个握手报文就能协商好各自的数据流的顺序号。一般来说， ISN 随时间而变化，因此每个连接都将具有不同的ISN 。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><strong style="line-height: 25px;">关闭一个 TCP 连接</strong></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TCP 连接建立起来后，就可以在两个方向传送数据流。当 TCP 的应用进程再没有数据需要发送时，就发关闭命令。 TCP 通过发送控制位 FIN=1 的数据片来关闭本方数据流，但还可以继续接收数据，直到对方关闭那个方向的数据流，连接就关闭。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TCP 协议使用修改的三次握手协议来关闭连接， 如图 3-11 所示，即终止一个连接要经过 4 次握手。这是因为 TCP 的半关闭（ half-close ）造成的。由于一个 TCP 连接是全双工（即数据在两个方向上能同时传递），因此每个方向必须单独地进行关闭。关闭的原则就是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向连接。当一端收到一个 FIN ，它必须通知应用层另一端已经终止了那个方向的数据传送。发送 FIN 通常是应用层进行关闭的结果。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/1490410001683742406.jpg" border="0" alt="" width="471" height="340" /><br /></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　从一方的 TCP 来说，连接的关闭有三种情况：</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　本方启动关闭</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　收到本方应用进程的关闭命令后， TCP 在发送完尚未处理的报文段后，发 FIN ＝ 1 的报文段给对方，且 TCP 不再受理本方应用进程的数据发送。在 FIN 以前发送的数据字节，包括 FIN ，都需要对方确认，否则要重传。注意 FIN 也占一个顺序号。一旦收到对方对 FIN 的确认以及对方的 FIN 报文段，本方 TCP 就对该 FIN 进行确认，在等待一段时间，然后关闭连接。等待是为了防止本方的确认报文丢失，避免对方的重传报文干扰新的连接。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　 对方启动关闭</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TCP 收到对方发来的 FIN 报文时，发 ACK 确认此 FIN 报文，并通知应用进程连接正在关闭。应用进程将以关闭命令响 应。 TCP 在发送完尚未处理的报文段后，发一个 FIN 报文给对方 TCP ，然后等待对方对 FIN 的确认，收到确认后关闭连接。若对方的确认未及时到达，在等待一段时间后也关闭连接。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　双方同时启动关闭</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 连接双方的应用进程同时发关闭命令，则双方 TCP 在发送完尚未处理的报文段后，发送 FIN 报文。各方 TCP 在 FIN 前所发报文都得到确认后，发 ACK 确认它收到的 FIN 。各方在收到对方对 FIN 的确认后，同样等待一段时间再关闭连接。这称之为同时关闭（ simultaneous close ）。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><strong style="line-height: 25px;">TCP 状态机</strong></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　TCP 协议的操作可以使用一个具有 11 种状态的有限状态机（ Finite State Machine ）来表示，图 3-12 描述了 TCP 的有限状态机，图中的圆角矩形表示状态，箭头表示状态之间的转换，各状态的描述如表 3-2 所示。图中用粗线表示客户端主动和被动的服务器端建立连接的正常过程：客户端的状态变迁用粗实线，服务器端的状态变迁用粗虚线。细线用于不常见的序列，如复位、同时打开、同时关闭等。图中的每条状态变换线上均标有&#8220;事件／动作&#8221;：事件是指用户执行了系统调用（ CONNECT 、 LISTEN 、 SEND 或 CLOSE ）、收到一个报文段（ SYN 、 FIN 、 ACK 或 RST ）、或者是出现了超过两倍最大的分组生命期的情况；动作是指发送一个报文段（ SYN 、 FIN 或 ACK ）或什么也没有（用&#8220;－&#8221;表示）。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/1490410001683742407.jpg" width="538" height="527" alt="" /><br /></p><p align="center" style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">图 3-12 TCP 有限状态机。粗实线表示客户的正常路径；<br style="line-height: 25px;" />粗虚线表示服务器的正常路径；细线表示不常见的事件。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　每个连接均开始于CLOSED 状态。当一方执行了被动的连接原语LISTEN或主动的连接原语CONNECT时，它便会脱离CLOSED状态。如果此时另一方执行了相对应的连接原语，连接便建立了，并且状态变为ESTABLISHED 。任何一方均可以首先请求释放连接，当连接被释放后，状态又回到了CLOSED 。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">表 3-2 TCP 状态表</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"></p><table border="1" style="line-height: 25px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; font-size: 14px; background-color: #dce7e6;"><tbody><tr><td><p align="center" style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">状 态</p></td><td><p align="center" style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">描 述</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">CLOSED</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">关闭状态，没有连接活动或正在进行</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">LISTEN</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">监听状态，服务器正在等待连接进入</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">SYN RCVD</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">收到一个连接请求，尚未确认</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">SYN SENT</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">已经发出连接请求，等待确认</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">ESTABLISHED</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">连接建立，正常数据传输状态</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">FIN WAIT 1</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">（主动关闭）已经发送关闭请求，等待确认</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">FIN WAIT 2</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">（主动关闭）收到对方关闭确认，等待对方关闭请求</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">TIMED WAIT</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">完成双向关闭，等待所有分组死掉</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">CLOSING</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">双方同时尝试关闭，等待对方确认</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">CLOSE WAIT</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">（被动关闭）收到对方关闭请求，已经确认</p></td></tr><tr><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">LAST ACK</p></td><td><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">（被动关闭）等待最后一个关闭确认，并等待所有分组死掉</p></td></tr></tbody></table><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><strong style="line-height: 25px;">1. 正常状态转换</strong></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">我们用图3-13 来显示在正常的TCP 连接的建立与终止过程中，客户与服务器所经历的不同状态。读者可以对照图 3-12 来阅读，使用图3-12 的状态图来跟踪图3-13 的状态变化过程，以便明白每个状态的变化：</p><ul style="line-height: 25px; margin: 5px 0px 5px 40px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">服务器端首先执行LISTEN 原语进入被动打开状态（LISTEN），等待客户端连接；</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">当客户端的一个应用程序发出CONNECT命令后，本地的TCP 实体为其创建一个连接记录并标记为SYN SENT 状态，然后给服务器发送一个SYN 报文段；</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">服务器收到一个 SYN 报文段，其TCP 实体给客户端发送确认 ACK 报文段同时发送一个 SYN 信号，进入 SYN RCVD 状态；</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">客户端收到SYN + ACK 报文段，其TCP 实体给服务器端发送出三次握手的最后一个 ACK 报文段，并转换为ESTABLISHED状态；</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">服务器端收到确认的ACK报文段，完成了三次握手，于是也进入ESTABLISHED 状态。</p></li></ul><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　 在此状态下，双方可以自由传输数据。当一个应用程序完成数据传输任务后，它需要关闭TCP连接。假设仍由客户端发起主动关闭连接。</p><ul style="line-height: 25px; margin: 5px 0px 5px 40px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">客户端执行CLOSE原语，本地的TCP 实体发送一个FIN 报文段并等待响应的确认进入状态FIN WAIT 1 ；</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">服务器收到一个 FIN 报文段，它确认客户端的请求发回一个 ACK 报文段，进入CLOSE WAIT状态；</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">客户端收到确认ACK报文段，就转移到FIN WAIT 2状态，此时连接在一个方向上就断开了；</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">服务器端应用得到通告后，也执行CLOSE原语关闭另一个方向的连接，其本地TCP 实体向客户端发送一个 FIN 报文段，并进入LAST ACK 状态，等待最后一个 ACK 确认报文段；</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">客户端收到FIN报文段并确认，进入TIMED WAIT 状态，此时双方连接均已经断开，但TCP 要等待一个2倍报文段最大生存时间MSL（ Maximum Segment Lifetime ），确保该连接的所有分组全部消失，以防止出现确认丢失的情况。当定时器超时后，TCP 删除该连接记录，返回到初始状态（CLOSED）。</p></li><li><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;">服务器收到最后一个确认 ACK 报文段，其TCP 实体便释放该连接，并删除连接记录，返回到初始状态（ CLOSED ）。</p></li></ul><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/1490410001683742408.jpg" width="463" height="383" alt="" /><br /></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><strong style="line-height: 25px;">2. 同时打开：</strong></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">尽管发生的可能性极小，两个应用程序同时彼此执行主动打开的情况还是可能的。每一方必须发送一个 SYN ，且这些 SYN 必须传递给对方。这需要每一方使用一个对方周知的端口作为本地端口。例如，主机 A 中的一个应用程序使用本地端口 7777 ，并与主机 B 的端口 8888 执行主动打开。主机 B 中的应用程序则使用本地端口 8888 ，并与主机 A 的端口 7777 执行主动打开。 TCP 是特意设计为了可以处理同时打开，对于同时打开它仅建立一条连接而不是两条连接（其他的协议族，最突出的是 OSI 传输层，在这种情况下将建立两条连接而不是一条连接）。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　当出现同时打开的情况时，状态变迁与图 3-13 所示的不同。两端几乎在同时发送 SYN ，并进入 SYN_SENT 状态。当每一端收到 SYN 时，状态变为 SYN_RCVD ，同时它们都再发 SYN 并对收到的 SYN 进行确认。当双方都收到 SYN 及相应的 ACK 时，状态都变迁为 ESTABLISHED 。图 3-14 显示了这些状态变迁过程。</p><div align="center" forimg="1" style="line-height: 25px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;"><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/1490410001683742409.jpg" width="545" height="175" alt="" /><br /></p></div><p align="center" style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">图 3-14 同时打开期间报文段的交换</p><p align="center" style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　一个同时打开的连接需要交换 4 个报文段，比正常的三次握手多一个。此外，要注意的是我们没有将任何一端称为客户或服务器，因为每一端既是客户又是服务器。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><strong style="line-height: 25px;">3. 同时关闭：</strong></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 正常情况下都是由一方（通常但不总是客户方）发送第一个 FIN 执行主动关闭，但双方都执行主动关闭也是可能的， TCP 协议也允许这样的同时关闭。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在图3-12 中，当两端应用层同时发出关闭命令时，两端均从ESTABLISHED 变为FIN_WAIT_1 。这将导致双方各发送一个FIN ，两个FIN 经过网络传送后分别到达另一端。收到FIN 后，状态由FIN_WAIT_1变迁到 CLOSING ，并发送最后的ACK 。当收到最后的ACK 时，状态变化为TIME_WAIT 。图3-15 总结了这些状态的变化，从图中可以看出同时关闭与正常关闭使用的报文段交换数目相同。</p><div align="center" forimg="1" style="line-height: 25px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px;"><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/1490410001683742410.jpg" width="545" height="183" alt="" /><br /></p></div><p align="center" style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">图 3-15 同时关闭期间的报文段交换</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><strong style="line-height: 25px;">4. 其它情况：</strong></p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">服务方打开：从LISTEN到SYN_SENT的变迁是正确的，它由服务器端主动发出 SYN 报文段，但 Berkeley 版的 TCP 软件并不支持它。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">重置连接（复位）：只有当 SYN_RCVD 状态是从LISTEN 状态（正常情况）进入，而不是从SYN_SENT状态（同时打开）进入时，从SYN_RCVD 回到LISTEN 的状态变迁才是有效的。这意味着如果我们执行被动打开（进入 LISTEN ），收到一个 SYN ，发送一个带ACK 的SYN（进入 SYN_RCVD ），然后收到一个 RST ，而不是一个 ACK ，便又回到 LISTEN 状态并等待另一个连接请求的到来。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">快速关闭：在主动关闭后的FIN_WAIT_1状态，如果收到的报文段不仅是ACK ，而且还包括对方的 FIN 信号，则直接进入 TIME_WAIT 状态，给对方发送 ACK 报文段，然后等待超时。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　另外， TIME_WAIT 状态的等待超时需要再详细解释一下，因为它直接影响到网络应用程序的表现。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">每个具体 TCP 实现必须选择一个报文段最大生存时间 MSL （ Maximum Segment Lifetime ），它是任何报文段被丢弃前在网络内的最长时间。我们知道这个时间是有限的，因为 TCP 报文段以 IP 数据报在网络内传输，而 IP 数据报有限制其生存时间的 TTL 字段。 RFC 793 [Postel 1981c ] 指出 MSL 为 2 分钟。然而，实现中的常用值是 30 秒、 1 分钟、或 2 分钟。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　对一个具体实现所给定的 MSL 值，处理的原则是：当 TCP 执行一个主动关闭，并发回最后一个 ACK ，该连接必须在 TIME_WAIT 状态停留的时间为 2 倍的 MSL ，因此 TIME_WAIT 状态也称为 2MSL 等待状态。在这段时间内，如果最后的 ACK 丢失，对方会超时并重发最后的 FIN ，这样本地 TCP 可以再次发送 ACK 报文段（这也是它唯一可以发送的报文，并重置 2MSL 定时器）。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　这种 2MSL 等待的另一个结果是这个 TCP 连接在 2MSL 等待期间，定义这个连接的套接字（ socket ，客户的 IP 地址和端口号，服务器的 IP 地址和端口号）不能再被使用。这个连接只能在 2MSL 结束后才能再被使用。在连接处于 2MSL 等待时，任何迟到的报文段将被丢弃。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;">　　我们假设图 3-12 中是客户执行主动关闭并进入 TIME_WAIT ，这是正常的情况，因为服务器通常执行被动关闭，不会进入 TIME_WAIT 状态。这暗示如果我们终止一个客户程序，并立即重新启动这个客户程序，则这个新客户程序将不能重用相同的本地端口。这不会带来什么问题，因为客户使用本地端口，而并不关心这个端口号是什么。然而，对于服务器，情况就有所不同，因为服务器使用周知端口。如果我们终止一个已经建立连接的服务器程序，并试图立即重新启动这个服务器程序，服务器程序将不能把它的这个周知端口赋值给它的端点，因为那个端口是处于 2MSL 连接的一部分。在重新启动服务器程序前，它需要在 1~4 分钟。这就是很多网络服务器程序被杀死后不能够马上重新启动的原因（错误提示为&#8220; Address already in use &#8221;）。</p><p style="line-height: 28px; margin: 0px 0px 10px; padding: 0px; color: #333333; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #dce7e6;"><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/1163899028699843054.jpg" width="731" height="1032" alt="" /><br /></p>转自：<a href="http://blog.163.com/tyw_andy/blog/static/1167902120099293916894/">http://blog.163.com/tyw_andy/blog/static/1167902120099293916894/</a><img src ="http://www.cppblog.com/flyinghare/aggbug/200888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2013-06-08 18:09 <a href="http://www.cppblog.com/flyinghare/archive/2013/06/08/200888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编获取CPU机器周期个数</title><link>http://www.cppblog.com/flyinghare/archive/2012/09/07/189851.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Fri, 07 Sep 2012 11:17:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2012/09/07/189851.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/189851.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2012/09/07/189851.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/189851.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/189851.html</trackback:ping><description><![CDATA[<p style="color: #333333; text-align: left; background-color: #ffffff; font-size: 13px; line-height: 19px; margin-top: 10px; margin-bottom: 10px; text-indent: 20px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; ">在Pentium以上的CPU中，提供了一条机器指令RDTSC（Read Time Stamp Counter）来读取这个时间戳的数字，并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数返回值的寄存器，所以我们可以把这条指令看成是一个普通的函数调用。vc2003像这样：<br />inline unsigned __int64 GetTimeStampCount()<br />{<br />__asm RDTSC<br />}<br />对于vc6或者其他编译器可能不行，因为RDTSC不被C++的内嵌汇编器直接支持，所以我们要用_emit伪指令直接嵌入该指令的机器码形式0X0F、0X31，如下：<br />inline unsigned __int64 GetTimeStampCount()<br />{<br />__asm _emit 0x0F<br />__asm _emit 0x31<br />}</p><p style="color: #333333; text-align: left; background-color: #ffffff; font-size: 13px; line-height: 19px; margin-top: 10px; margin-bottom: 10px; text-indent: 20px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; ">&nbsp;</p><p style="color: #333333; text-align: left; background-color: #ffffff; font-size: 13px; line-height: 19px; margin-top: 10px; margin-bottom: 10px; text-indent: 20px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; ">&nbsp;</p><p style="color: #333333; text-align: left; background-color: #ffffff; font-size: 13px; line-height: 19px; margin-top: 10px; margin-bottom: 10px; text-indent: 20px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; ">对关注性能的程序开发人员而言，一个好的计时部件既是益友，也是良师。计时器既可以作为程序组件帮助程序员精确的控制程序进程，又是一件有力的调试武器，在有经验的程序员手里可以尽快的确定程序的性能瓶颈，或者对不同的算法作出有说服力的性能比较。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 　　在Windows平台下，常用的计时器有两种，一种是timeGetTime多媒体计时器，它可以提供毫秒级的计时。但这个精度对很多应用场合而言还是太粗糙了。另一种是QueryPerformanceCount计数器，随系统的不同可以提供微秒级的计数。对于实时图形处理、多媒体数据流处理、或者实时系统构造的程序员，善用QueryPerformanceCount/QueryPerformanceFrequency是一项基本功。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 　　本文要介绍的，是另一种直接利用Pentium&nbsp;&nbsp; CPU内部时间戳进行计时的高精度计时手段。以下讨论主要得益于《Windows图形编程》一书，第&nbsp;&nbsp; 15页－17页，有兴趣的读者可以直接参考该书。关于RDTSC指令的详细讨论，可以参考Intel产品手册。本文仅仅作抛砖之用。&nbsp;&nbsp;&nbsp;<br />&nbsp; 　　在&nbsp;&nbsp; Intel&nbsp;&nbsp; Pentium以上级别的CPU中，有一个称为&#8220;时间戳（Time&nbsp;&nbsp; Stamp）&#8221;的部件，它以64位无符号整型数的格式，记录了自CPU上电以来所经过的时钟周期数。由于目前的CPU主频都非常高，因此这个部件可以达到纳秒级的计时精度。这个精确性是上述两种方法所无法比拟的。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 　　在Pentium以上的CPU中，提供了一条机器指令RDTSC（Read&nbsp;&nbsp; Time&nbsp;&nbsp; Stamp&nbsp;&nbsp; Counter）来读取这个时间戳的数字，并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数返回值的寄存器，所以我们可以把这条指令看成是一个普通的函数调用。像这样：&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; inline&nbsp;&nbsp; unsigned&nbsp;&nbsp; __int64&nbsp;&nbsp; GetCycleCount()&nbsp;&nbsp;&nbsp;<br />&nbsp; {&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; __asm&nbsp;&nbsp; RDTSC&nbsp;&nbsp;&nbsp;<br />&nbsp; }&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 但是不行，因为RDTSC不被C++的内嵌汇编器直接支持，所以我们要用_emit伪指令直接嵌入该指令的机器码形式0X0F、0X31，如下：&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; inline&nbsp;&nbsp; unsigned&nbsp;&nbsp; __int64&nbsp;&nbsp; GetCycleCount()&nbsp;&nbsp;&nbsp;<br />&nbsp; {&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; __asm&nbsp;&nbsp; _emit&nbsp;&nbsp; 0x0F&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; __asm&nbsp;&nbsp; _emit&nbsp;&nbsp; 0x31&nbsp;&nbsp;&nbsp;<br />&nbsp; }&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 以后在需要计数器的场合，可以像使用普通的Win32&nbsp;&nbsp; API一样，调用两次GetCycleCount函数，比较两个返回值的差，像这样：&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; unsigned&nbsp;&nbsp; long&nbsp;&nbsp; t;&nbsp;&nbsp;&nbsp;<br />&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; (unsigned&nbsp;&nbsp; long)GetCycleCount();&nbsp;&nbsp;&nbsp;<br />&nbsp; //Do&nbsp;&nbsp; Something&nbsp;&nbsp; time-intensive&nbsp;&nbsp; ...&nbsp;&nbsp;&nbsp;<br />&nbsp; t&nbsp;&nbsp; -=&nbsp;&nbsp; (unsigned&nbsp;&nbsp; long)GetCycleCount();&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 　　《Windows图形编程》第15页编写了一个类，把这个计数器封装起来。有兴趣的读者可以去参考那个类的代码。作者为了更精确的定时，做了一点小小的改进，把执行RDTSC指令的时间，通过连续两次调用GetCycleCount函数计算出来并保存了起来，以后每次计时结束后，都从实际得到的计数中减掉这一小段时间，以得到更准确的计时数字。但我个人觉得这一点点改进意义不大。在我的机器上实测，这条指令大概花掉了几十到100多个周期，在&nbsp;&nbsp; Celeron&nbsp;&nbsp; 800MHz的机器上，这不过是十分之一微秒的时间。对大多数应用来说，这点时间完全可以忽略不计；而对那些确实要精确到纳秒数量级的应用来说，这个补偿也过于粗糙了。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 这个方法的优点是：&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 1.高精度。可以直接达到纳秒级的计时精度（在1GHz的CPU上每个时钟周期就是一纳秒），这是其他计时方法所难以企及的。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 2.&nbsp;&nbsp; 成本低。timeGetTime&nbsp;&nbsp; 函数需要链接多媒体库winmm.lib，QueryPerformance*&nbsp;&nbsp; 函数根据MSDN的说明，需要硬件的支持（虽然我还没有见过不支持的机器）和KERNEL库的支持，所以二者都只能在Windows平台下使用（关于DOS平台下的高精度计时问题，可以参考《图形程序开发人员指南》，里面有关于控制定时器8253的详细说明）。但RDTSC指令是一条CPU指令，凡是i386平台下Pentium以上的机器均支持，甚至没有平台的限制（我相信i386版本UNIX和Linux下这个方法同样适用，但没有条件试验），而且函数调用的开销是最小的。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 3.&nbsp;&nbsp; 具有和CPU主频直接对应的速率关系。一个计数相当于1/(CPU主频Hz数)秒，这样只要知道了CPU的主频，可以直接计算出时间。这和&nbsp;&nbsp; QueryPerformanceCount不同，后者需要通过QueryPerformanceFrequency获取当前计数器每秒的计数次数才能换算成时间。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 这个方法的缺点是：&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 1.现有的C/C++编译器多数不直接支持使用RDTSC指令，需要用直接嵌入机器码的方式编程，比较麻烦。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 2.数据抖动比较厉害。其实对任何计量手段而言，精度和稳定性永远是一对矛盾。如果用低精度的timeGetTime来计时，基本上每次计时的结果都是相同的；而RDTSC指令每次结果都不一样，经常有几百甚至上千的差距。这是这种方法高精度本身固有的矛盾。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 关于这个方法计时的最大长度，我们可以简单的用下列公式计算：&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 自CPU上电以来的秒数&nbsp;&nbsp; =&nbsp;&nbsp; RDTSC读出的周期数&nbsp;&nbsp; /&nbsp;&nbsp; CPU主频速率（Hz）&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 64位无符号整数所能表达的最大数字是1.8&#215;10^19，在我的Celeron&nbsp;&nbsp; 800上可以计时大约700年（书中说可以在200MHz的Pentium上计时117年，这个数字不知道是怎么得出来的，与我的计算有出入）。无论如何，我们大可不必关心溢出的问题。&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 下面是几个小例子，简要比较了三种计时方法的用法与精度&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; //Timer1.cpp&nbsp;&nbsp; 使用了RDTSC指令的Timer类//KTimer类的定义可以参见《Windows图形编程》P15&nbsp;&nbsp;&nbsp;<br />&nbsp; //编译行：CL&nbsp;&nbsp; Timer1.cpp&nbsp;&nbsp; /link&nbsp;&nbsp; USER32.lib&nbsp;&nbsp;&nbsp;<br />&nbsp; #include&nbsp;&nbsp; &amp;ltstdio.h&gt;&nbsp;&nbsp;&nbsp;<br />&nbsp; #include&nbsp;&nbsp; "KTimer.h"&nbsp;&nbsp;&nbsp;<br />&nbsp; main()&nbsp;&nbsp;&nbsp;<br />&nbsp; {&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; unsigned&nbsp;&nbsp; t;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; KTimer&nbsp;&nbsp; timer;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; timer.Start();&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; Sleep(1000);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; timer.Stop();&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("Lasting&nbsp;&nbsp; Time:&nbsp;&nbsp; %d\n",t);&nbsp;&nbsp;&nbsp;<br />&nbsp; }&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; //Timer2.cpp&nbsp;&nbsp; 使用了timeGetTime函数&nbsp;&nbsp;&nbsp;<br />&nbsp; //需包含&amp;ltmmsys.h&gt;，但由于Windows头文件错综复杂的关系&nbsp;&nbsp;&nbsp;<br />&nbsp; //简单包含&amp;ltwindows.h&gt;比较偷懒：）&nbsp;&nbsp;&nbsp;<br />&nbsp; //编译行：CL&nbsp;&nbsp; timer2.cpp&nbsp;&nbsp; /link&nbsp;&nbsp; winmm.lib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; #include&nbsp;&nbsp; &amp;ltwindows.h&gt;&nbsp;&nbsp;&nbsp;<br />&nbsp; #include&nbsp;&nbsp; &amp;ltstdio.h&gt;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; main()&nbsp;&nbsp;&nbsp;<br />&nbsp; {&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp; t1,&nbsp;&nbsp; t2;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; t1&nbsp;&nbsp; =&nbsp;&nbsp; timeGetTime();&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; Sleep(1000);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; t2&nbsp;&nbsp; =&nbsp;&nbsp; timeGetTime();&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("Begin&nbsp;&nbsp; Time:&nbsp;&nbsp; %u\n",&nbsp;&nbsp; t1);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("End&nbsp;&nbsp; Time:&nbsp;&nbsp; %u\n",&nbsp;&nbsp; t2);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("Lasting&nbsp;&nbsp; Time:&nbsp;&nbsp; %u\n",(t2-t1));&nbsp;&nbsp;&nbsp;<br />&nbsp; }&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; //Timer3.cpp&nbsp;&nbsp; 使用了QueryPerformanceCounter函数&nbsp;&nbsp;&nbsp;<br />&nbsp; //编译行：CL&nbsp;&nbsp; timer3.cpp&nbsp;&nbsp; /link&nbsp;&nbsp; KERNEl32.lib&nbsp;&nbsp;&nbsp;<br />&nbsp; #include&nbsp;&nbsp; &amp;ltwindows.h&gt;&nbsp;&nbsp;&nbsp;<br />&nbsp; #include&nbsp;&nbsp; &amp;ltstdio.h&gt;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; main()&nbsp;&nbsp;&nbsp;<br />&nbsp; {&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; LARGE_INTEGER&nbsp;&nbsp; t1,&nbsp;&nbsp; t2,&nbsp;&nbsp; tc;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; QueryPerformanceFrequency(&amp;tc);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("Frequency:&nbsp;&nbsp; %u\n",&nbsp;&nbsp; tc.QuadPart);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; QueryPerformanceCounter(&amp;t1);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; Sleep(1000);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; QueryPerformanceCounter(&amp;t2);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("Begin&nbsp;&nbsp; Time:&nbsp;&nbsp; %u\n",&nbsp;&nbsp; t1.QuadPart);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("End&nbsp;&nbsp; Time:&nbsp;&nbsp; %u\n",&nbsp;&nbsp; t2.QuadPart);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("Lasting&nbsp;&nbsp; Time:&nbsp;&nbsp; %u\n",(&nbsp;&nbsp; t2.QuadPart-&nbsp;&nbsp; t1.QuadPart));&nbsp;&nbsp;&nbsp;<br />&nbsp; }&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; ////////////////////////////////////////////////&nbsp;&nbsp;&nbsp;<br />&nbsp; //以上三个示例程序都是测试1秒钟休眠所耗费的时间&nbsp;&nbsp;&nbsp;<br />&nbsp; file://测/试环境：Celeron&nbsp;&nbsp; 800MHz&nbsp;&nbsp; /&nbsp;&nbsp; 256M&nbsp;&nbsp; SDRAM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows&nbsp;&nbsp; 2000&nbsp;&nbsp; Professional&nbsp;&nbsp; SP2&nbsp;&nbsp;&nbsp;<br />&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Microsoft&nbsp;&nbsp; Visual&nbsp;&nbsp; C++&nbsp;&nbsp; 6.0&nbsp;&nbsp; SP5&nbsp;&nbsp;&nbsp;<br />&nbsp; ////////////////////////////////////////////////&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 以下是Timer1的运行结果，使用的是高精度的RDTSC指令&nbsp;&nbsp;&nbsp;<br />&nbsp; Lasting&nbsp;&nbsp; Time:&nbsp;&nbsp; 804586872&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 以下是Timer2的运行结果，使用的是最粗糙的timeGetTime&nbsp;&nbsp; API&nbsp;&nbsp;&nbsp;<br />&nbsp; Begin&nbsp;&nbsp; Time:&nbsp;&nbsp; 20254254&nbsp;&nbsp;&nbsp;<br />&nbsp; End&nbsp;&nbsp; Time:&nbsp;&nbsp; 20255255&nbsp;&nbsp;&nbsp;<br />&nbsp; Lasting&nbsp;&nbsp; Time:&nbsp;&nbsp; 1001&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; 以下是Timer3的运行结果，使用的是QueryPerformanceCount&nbsp;&nbsp; API&nbsp;&nbsp;&nbsp;<br />&nbsp; Frequency:&nbsp;&nbsp; 3579545&nbsp;&nbsp;&nbsp;<br />&nbsp; Begin&nbsp;&nbsp; Time:&nbsp;&nbsp; 3804729124&nbsp;&nbsp;&nbsp;<br />&nbsp; End&nbsp;&nbsp; Time:&nbsp;&nbsp; 3808298836&nbsp;&nbsp;&nbsp;<br />&nbsp; Lasting&nbsp;&nbsp; Time:&nbsp;&nbsp; 3569712 &nbsp;<br /><br />转自：<a href="http://blog.csdn.net/bbs598598/article/details/7441687">http://blog.csdn.net/bbs598598/article/details/7441687</a></p><img src ="http://www.cppblog.com/flyinghare/aggbug/189851.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2012-09-07 19:17 <a href="http://www.cppblog.com/flyinghare/archive/2012/09/07/189851.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MFC子窗口和父窗口（SetParent,SetOwner）</title><link>http://www.cppblog.com/flyinghare/archive/2012/08/17/187496.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Fri, 17 Aug 2012 09:36:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2012/08/17/187496.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/187496.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2012/08/17/187496.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/187496.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/187496.html</trackback:ping><description><![CDATA[<span style="font-family: verdana; font-size: 12px; ">一、概念和区别&nbsp;</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">在windows系统中，每个窗口对象都对应有一个数据结构，形成一个list链表。系统的窗口管理器通过这个list来获取窗口信息和管理每个窗口。这个数据结构中有四个数据用来构建list，即child、sibling、parent、owner四个域。</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">所以我们可以看到，窗口之间的关系有两种：owner-owned 关系和 parent-child关系。前者称之为拥有/被拥有关系，后者称之为父/子关系。在这篇文字中，我把owner窗口称之所有者窗口。换句话说，一个窗口在有一个父窗口（parent)的同时，还可能被不同的窗口拥有（owner)，也可以有自己的子窗口(child)。在MFC 的CWnd类中，所有者窗口保存在m_hWndOwner成员变量中，父窗口则保存在m_hParent中，但是这两个值并不一定和窗口对象数据结构中的值相对应。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">窗口之间的关系，决定了窗口的外在表现。比如显示、销毁等。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">如果一个窗口数据的owner域非NULL，则它和该窗口建立了owner-owned 关系，拥有关系决定了：</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（1）被拥有的窗口永远显示在拥有它的那个窗口的前面；</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（2）当所有者窗口最小化的时候，它所拥有的窗口都会被隐藏；</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（3）当所有者窗口被销毁的时候，它所拥有的窗口都会被销毁。</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">需要注意的是，隐藏所有者窗口并不会影响它所拥有的窗口的可见状态。比如：如果窗口 A 拥有窗口B,窗口B拥有窗口C,则当窗口A最小化的时候，窗口B被隐藏，但是窗口 C还是可见。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">如果一个窗口的parent域非NULL，则它和该窗口之间就建立了parent-child关系。父子决定了：</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（1）窗口在屏幕上面的显示位置。父窗口提供了用来定位子窗口的坐标系统，一个子窗口只能显示在它的父窗口的客户区中，之外的部分将被裁减。这个裁减法则决定了如果父窗口不可见，则子窗口肯定不可见。如果父窗口移动到了屏幕之外，子窗口也一样。</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（2）当父窗口被隐藏时，它的所有子窗口也被隐藏。</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（3）父窗口被销毁的时候，它所拥有的子窗口都会被销毁。</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">注意！最小化父窗口不会影响子窗口的可见状态，子窗口会随着父窗口被最小化，但是它的WS_VISIBLE属性不会变。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">Windows系统为什么要使用两种关系呢？这是为了更加灵活的管理窗口。举个例子：组合框（combobox)的下拉列表框（list box）可以超出组合框的父窗口的客户区，这样有利于显示，因此系统创建该list box的时候，是作为控制台窗口（desktop window）的子窗口，它的父窗口hWndParent是NULL，这样，list box的显示区域是限制在整个屏幕内，但是该list box的所有者却是组合框的第一个非子窗口祖先（比如对话框），当它的所有者窗口销毁后，该 list box自动销毁。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">另外，窗口之间消息的传递也和窗口关系有关，通常，一个窗口会把自己的通知消息发送给它的父窗口，但不全是这样，比如，CToolBar发送通知消息给它的所有者窗口而不是父窗口。这样以来，就可以允许工具条作为一个窗口（比如一个 OLE 容器程序窗口）的子窗口的同时，能够给另一个窗口（比如in-place框架窗口）发送消息。至于某类窗口到底是把消息发送给谁，是父窗口还是所有者窗口，microsoft并没有明示。还有，在现场（in-place）编辑的情况下，当一个 server 窗口激活或者失效的时候，框架窗口所拥有的子窗口自动隐藏或者显示，这也是通过直接调用SetOwner函数实现的。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">二、窗口类型的说明和限制</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（1）控制台窗口（desktop window）。这是系统最早创建的窗口。可以认为它是所有 WS_OVERLAPPED 类型窗口的所有者和父窗口。Kyle Marsh在他的文章&#8220;Win32 Window Hierarchy and Styles&#8221;中指出，当系统初始化的时候，它首先创建控制台窗口，大小覆盖整个屏幕。所有其它窗口都在这个控制台窗口上面显示。窗口管理器所用的窗口list中第一个就是这个控制台。它的下一层窗口叫做顶级窗口（top-level），顶级窗口是指所有非child、没有父窗口，或者父窗口是desktop的窗口，它们没有WS_CHILD属性。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（2）WS_OVERLAPPED类型的窗口可以显示在屏幕的任何地方。它们的所有者窗口是控制台。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">Overlapped 类型的窗口属于顶级窗口，一般作为应用程序的主窗口。不论是否给出了WS_CAPTION、WS_BORDER属性，这类窗口创建后都有标题栏和边框。Overlapped窗口可以拥有其它顶级窗口或者被其它顶级窗口所拥有。所有overlapped窗口都有WS_CLIPSIBLINGS属性。系统可以自动设置 overlapped窗口的大小和初始位置。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">当系统 shuts down的时候，它将销毁所有overlapped类型的窗口。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（3）WS_POPUP类型的窗口可以显示在屏幕任何地方，它们一般没有父窗口，但是如果明确调用SetParent，这类窗口也可以有父窗口。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">WS_POPUP类型的窗口的所有者是在CreateWindow函数中通过设置hWndParent参数给定的，如果hWndParent不是子窗口，则该窗口就成为这个新的弹出式窗口的owner，否则，系统从hWndParent的父窗口向上找，直到找到第一个非子窗口，把它作为该弹出窗口的owner。当owner窗口销毁的时候，系统自动销毁这个弹出窗口。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">Pop-up类型的窗口也属于顶级窗口，它和 overlapped 窗口的主要区别是弹出式窗口不需要有标题栏，也不必有边框。弹出式可以拥有其它顶级窗口或者被拥有。所有弹出式窗口也都有 WS_CLIPSIBLINGS属性。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（4）所有者窗口（owner)只能是 overlapped 或者 pop-up 类型的窗口，子窗口不能是所有者窗口，也就是说子窗口不能拥有其它窗口。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">overlapped 或者 pop-up 类型的窗口在拥有其它窗口的同时，也可以被拥有。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">在使用CreateWindowEx创建 WS_OVERLAPPED 或者 WS_POPUP类型的窗口时，可以在 hwndParent 参数中给出它的所有者窗口的句柄。如果 hwndParent 给出的是一个child 类型的窗口句柄，则系统自动将新创建窗口的所有权交给该子窗口的顶级父窗口。在这种情况下，参数hwndParent被保存在新建窗口的parent域中，而它的所有者窗口句柄则保存在owner域中。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（5）缺省情况下，对话框和消息框属于 owned 窗口，除非在创建它们的时候明确给出了WS_CHILD属性，（比如对话框中嵌入对话框的情形）</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">否则由系统负责给它们指定owner窗口。需要注意的是，一旦创建了owned类型的窗口，就无法再改变其所有关系，因为WIN32没有没有提供改变窗口所有者的方法。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">而且在Win32中，由于有多线程的存在，所以要注意保证父子窗口或者owner/owned 窗口要同属于一个线程。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（6）对于 WS_CHILD类型的窗口，它的父窗口就是它的所有者窗口。一个子窗口的父窗口也是在CreateWindow函数中用hWndParent参数指定的。子窗口只能在父窗口的客户区中显示，并随父窗口一起销毁。</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">子窗口必须有一个父窗口，这是它和overlapped 以及 pop-up 窗口之间的主要区别。父窗口可以是顶级窗口，也可以是其它子窗口。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">三、几个相关函数的说明</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（1）获取/设置所有者窗口</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">win32 API提供了函数GetWindow函数（GW_OWNER 标志）来获取一个窗口的所有者窗口句柄。</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">GetWindow(hWnd, GW_OWNER)永远返回窗口的所有者(owner)。对于子窗口，函数返回 NULL，因为它们的父窗口就相当于所有者（注意，是&#8220;相当于&#8221;）。因为Windows系统没有维护子窗口的所有者信息。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">MFC中则是通过如下函数得到所有者窗口指针：</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">_AFXWIN_INLINE CWnd* CWnd::GetOwner() const</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">{ return m_hWndOwner != NULL ? CWnd::FromHandle(m_hWndOwner) : GetParent(); }</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">从上述代码我们可以看出，它返回的值和GetWindow返回的有所区别，如果当前窗口没有owner，那么将返回它的父窗口指针。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">但是Windows没有提供改变窗口所有者的方法。MFC中则提供了改变所有者的方法：</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">_AFXWIN_INLINE void CWnd::SetOwner(CWnd* pOwnerWnd)</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">{ m_hWndOwner = pOwnerWnd != NULL ? pOwnerWnd-&gt;m_hWnd : NULL; }</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">另外，mfc还提供了CWnd::GetSafeOwner( CWnd* pParent, HWND* pWndTop );函数，可以用来得到参数pParent的第一个非child属性的父窗口指针。如果这个参数是NULL，则返回当前线程的主窗口(通过AfxGetMainWnd得到)。框架经常使用这个函数查找对话框或者属性页的所有者窗口。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（2）获取/设置父窗口</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">WIN32 API给出了函数GetParent和SetParent。而mfc也是完全封装了这两个函数：</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">_AFXWIN_INLINE CWnd* CWnd::SetParent(CWnd* pWndNewParent)</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">{ ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::SetParent(m_hWnd,</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">pWndNewParent-&gt;GetSafeHwnd())); }</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">_AFXWIN_INLINE CWnd* CWnd::GetParent() const</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">{ ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::GetParent(m_hWnd)); }</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">对于SetParent，msdn里面说明了父子窗口必须是同一个进程的。但是由于窗口句柄是系统全局唯一的，不属于同一个进程的情况下，也可以成功调用，但是后果未知。</span><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">GetParent的返回值比较复杂，对于overlapped类型的窗口，它返回0，对于WS_CHILD类型，它返回其父窗口，对于WS_POPUP类型，它返回其所有者窗口，如果想得到创建它时所传递进去的那个hwndParent参数，应该用GetWindowWord(GWW_HWNDPARENT)函数。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（3）GetWindowWord(hWnd, GWW_HWNDPARENT)返回一个窗口的父窗口，如果没有，则返回其所有者。</span><br style="font-family: verdana; font-size: 12px; " /><br style="font-family: verdana; font-size: 12px; " /><span style="font-family: verdana; font-size: 12px; ">（4）上面谈到，当一个owner窗口被最小化后，系统自动隐藏它所拥有的窗口。当owner窗口被恢复的时候，系统自动显示它所拥有的窗口。在这两种情况下，系统都会发送（send）WM_SHOWWINDOW消息给被拥有的窗口。某些时候，我们可能需要隐藏 owned窗口，但并不想最小化其所有者窗口，这时候，可以通过ShowOwnedPopups函数来实现，该函数设置或者删除当前窗口所拥有的窗口的WS_VISIBLE属性，然后发送WM_SHOWWINDOW消息更新窗口显示。</span><br style="font-family: verdana; font-size: 12px; " />转自：<a href="http://www.cnblogs.com/BeyondTechnology/archive/2011/03/25/1995934.html">http://www.cnblogs.com/BeyondTechnology/archive/2011/03/25/1995934.html</a><img src ="http://www.cppblog.com/flyinghare/aggbug/187496.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2012-08-17 17:36 <a href="http://www.cppblog.com/flyinghare/archive/2012/08/17/187496.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>避免读管道时的无限阻塞 (.Net 的Processl类的StandardOutput流的可读状态监测)</title><link>http://www.cppblog.com/flyinghare/archive/2012/08/16/187371.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Thu, 16 Aug 2012 05:30:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2012/08/16/187371.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/187371.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2012/08/16/187371.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/187371.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/187371.html</trackback:ping><description><![CDATA[<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; ">因为QTP的需要，同事写了通过进程来调用Plink进行Telnet连接的接口。我测试的时候发现，他那个调用.Net 里面的Process进程的方法，通过重定向获取标准输出流的办法有点不好，就是调用了流动Read（）函数之后，就会一直阻塞在那里，知道流中有数据才能正确返回，而peek函数又不能正确的监测到流中是否有数据可以读。我先去翻翻了MSDN中那个StreamReader类的办法，好像确实没有办法，反倒是在Process的StandardOutput属性的说明那里，明显写着，如果标准输出里面没有数据的话，read函数就会无限时的阻塞在那里知道有数据可以读才行，然后他还提到了一些导致死锁的问题。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; ">我去写了个简单的.Net程序来测试了一下，可以知道那个StreamReader是一个FileStream来的，而且那个CanTimeout等属性都表明不是一个可以异步读取的流。难道真没有办法监测到这个流中是否有数据可读的状态吗？ 根据常识知道，这个流应该是&#8220;匿名管道&#8221;来的，去找了一下MSDN中关于管道的api函数，还真让我找到了一个，那就是PeekNamedPipe&nbsp;<a href="http://msdn.microsoft.com/en-us/library/aa365779(VS.85).aspx" style="color: #f87070; ">http://msdn.microsoft.com/en-us/library/aa365779(VS.85).aspx</a>。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; ">根据它的说明，来看看这个</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; ">The&nbsp;<strong>PeekNamedPipe</strong>&nbsp;function is similar to the&nbsp;<a href="http://msdn.microsoft.com/en-us/library/aa365467(v=VS.85).aspx" style="color: #f87070; "><strong>ReadFile</strong></a>&nbsp;function with the following exceptions:</p><ul style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; list-style-type: none; list-style-position: initial; list-style-image: initial; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "><li style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">The data is read in the mode specified with&nbsp;<a href="http://msdn.microsoft.com/en-us/library/aa365150(v=VS.85).aspx" style="color: #f87070; "><strong>CreateNamedPipe</strong></a>. For example, create a pipe with PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE. If you change the mode to PIPE_READMODE_BYTE with&nbsp;<a href="http://msdn.microsoft.com/en-us/library/aa365787(v=VS.85).aspx" style="color: #f87070; "><strong>SetNamedPipeHandleState</strong></a>,&nbsp;<a href="http://msdn.microsoft.com/en-us/library/aa365467(v=VS.85).aspx" style="color: #f87070; "><strong>ReadFile</strong></a>&nbsp;will read in byte mode, but&nbsp;<strong>PeekNamedPipe</strong>&nbsp;will continue to read in message mode.</li><li style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">The data read from the pipe is not removed from the pipe's buffer.</li><li style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">The function can return additional information about the contents of the pipe.</li><li style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">The function always returns immediately in a single-threaded application, even if there is no data in the pipe. The wait mode of a named pipe handle (blocking or nonblocking) has no effect on the function.</li></ul><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "><strong>Note</strong>&nbsp;&nbsp; The&nbsp;<strong>PeekNamedPipe</strong>&nbsp;function can block thread execution the same way any I/O function can when called on a synchronous handle in a multi-threaded application. To avoid this condition, use a pipe handle created for&nbsp;<a href="http://msdn.microsoft.com/en-us/library/aa365683(v=VS.85).aspx" style="color: #f87070; ">asynchronous I/O</a>.</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; ">If the specified handle is a named pipe handle in byte-read mode, the function reads all available bytes up to the size specified in&nbsp;<em>nBufferSize</em>. For a named pipe handle in message-read mode, the function reads the next message in the pipe. If the message is larger than&nbsp;<em>nBufferSize</em>, the function returns TRUE after reading the specified number of bytes. In this situation,&nbsp;<em>lpBytesLeftThisMessage</em>&nbsp;will receive the number of bytes remaining in the message.</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; ">这个函数不管命名管道是不是阻塞模式的，都会立即返回（除了在多线程环境下的某种情况下会阻塞，大概就是<a href="http://my.donews.com/yeyanbo/tag/peeknamedpipe/" style="color: #f87070; ">http://my.donews.com/yeyanbo/tag/peeknamedpipe/</a>这个文章发现的问题。），文档又说所有的&#8221;匿名管道&#8220;其实都是一个&#8220;命名管道&#8221;来实现的，所以操作&#8220;命名管道&#8221;的函数对&#8220;匿名管道&#8221;也是有效的。这个函数明显是我想要的，可以用来检测process标准输出流中是否有数据可以读，又不会阻塞。在vb.net的测试代码里面试了一下，应该是可以工作，测试代码如下：</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">Imports</span><span style="color: #000000; ">&nbsp;System.Diagnostics.Process<br /></span><span style="color: #0000FF; ">Public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Class</span><span style="color: #000000; ">&nbsp;Form1<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">Declare</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Function</span><span style="color: #000000; ">&nbsp;SetNamedPipeHandleState&nbsp;</span><span style="color: #0000FF; ">Lib</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">kernel32</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">ByVal</span><span style="color: #000000; ">&nbsp;hNamedPipe&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">ByRef</span><span style="color: #000000; ">&nbsp;lpMode&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">ByRef</span><span style="color: #000000; ">&nbsp;lpMaxCollectionCount&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">ByRef</span><span style="color: #000000; ">&nbsp;lpCollectDataTimeout&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">Declare</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Function</span><span style="color: #000000; ">&nbsp;PeekNamedPipe&nbsp;</span><span style="color: #0000FF; ">Lib</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">kernel32</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">ByVal</span><span style="color: #000000; ">&nbsp;hNamedPipe&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">ByRef</span><span style="color: #000000; ">&nbsp;lpBuffer&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">ByVal</span><span style="color: #000000; ">&nbsp;nBufferSize&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">ByRef</span><span style="color: #000000; ">&nbsp;lpBytesRead&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">ByRef</span><span style="color: #000000; ">&nbsp;lpTotalBytesAvail&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">ByRef</span><span style="color: #000000; ">&nbsp;lpBytesLeftThisMessage&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">Private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Sub</span><span style="color: #000000; ">&nbsp;Button1_Click(</span><span style="color: #0000FF; ">ByVal</span><span style="color: #000000; ">&nbsp;sender&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;System.Object,&nbsp;</span><span style="color: #0000FF; ">ByVal</span><span style="color: #000000; ">&nbsp;e&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;System.EventArgs)&nbsp;</span><span style="color: #0000FF; ">Handles</span><span style="color: #000000; ">&nbsp;Button1.Click<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">Dim</span><span style="color: #000000; ">&nbsp;p&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;Process&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">New</span><span style="color: #000000; ">&nbsp;Process<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StartInfo.FileName&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">c:\windows\system32\cmd.exe</span><span style="color: #000000; ">"</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StartInfo.RedirectStandardInput&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">True</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StartInfo.RedirectStandardOutput&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">True</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StartInfo.UseShellExecute&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">False</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.Start()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StandardInput.WriteLine(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">hostname</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">Dim</span><span style="color: #000000; ">&nbsp;f&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;System.IO.FileStream&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;p.StandardOutput.BaseStream<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">Dim</span><span style="color: #000000; ">&nbsp;mode&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">&nbsp;no-wait</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">Dim</span><span style="color: #000000; ">&nbsp;count&nbsp;</span><span style="color: #0000FF; ">As</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">Integer</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">修改这个命名管道为&nbsp;异步的，是不能成功的</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">'mode&nbsp;=&nbsp;SetNamedPipeHandleState(f.Handle,&nbsp;mode,&nbsp;System.IntPtr.Zero,&nbsp;System.IntPtr.Zero)</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">不过这个PeekNamePipe&nbsp;函数是可以得到&nbsp;管道里面有多少字节可以读到，执行完之后count里面是对的，可以读取的数据</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;PeekNamedPipe(f.Handle,&nbsp;System.IntPtr.Zero,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;System.IntPtr.Zero,&nbsp;count,&nbsp;System.IntPtr.Zero)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StandardOutput.Read()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">While</span><span style="color: #000000; ">&nbsp;p.StandardOutput.Peek&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StandardOutput.Read()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">End</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">While</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">在这个地方的时候就不能再read了，read就无限阻塞直到有数据来才能返回了。</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;p.StandardOutput.Peek()&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">这个时候的peek返回&nbsp;-1是对的</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;PeekNamedPipe(f.Handle,&nbsp;System.IntPtr.Zero,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;System.IntPtr.Zero,&nbsp;count,&nbsp;System.IntPtr.Zero)&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">这个count得到0&nbsp;是对的，管道里面没有消息的了</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StandardInput.WriteLine(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">hostname</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;p.StandardOutput.Peek()&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">这个还是返回&nbsp;-1是不对的，</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;PeekNamedPipe(f.Handle,&nbsp;System.IntPtr.Zero,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;System.IntPtr.Zero,&nbsp;count,&nbsp;System.IntPtr.Zero)&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">这个返回正确的count，表明管道里面有数据是对的</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StandardOutput.Read()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.StandardOutput.Peek()&nbsp;</span><span style="color: #008000; ">'</span><span style="color: #008000; ">peek函数一定要在read成功调用过一次之后才能正确的得到管道的状态。但Read一次又可能引起无限时间的阻塞！！！！所以只有PeekNamedPipe才能正确的无阻塞的检测到管道的数据</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">End&nbsp;Sub</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">End&nbsp;Class</span></div></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "><strong>总结一下 ：感觉。Net对这个&#8220;命名管道&#8220;&#8221;匿名管道&#8220;的支持明显不够，API中都有监测到管道是否有数据可以读到函数。.Net里面却连管道对应的类都没有实现，所以相应的这种阻塞情况就也没法处理了。可能这部分的封装有待完善吧。</strong></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "><strong>后来有用Reflector工具反汇编看了看系统Process几个类的相应实现代码，可以看到他是CreatePipe创建一个管道，然后DuplicateHandle复制了一个文件句柄的，跟我事先的猜测是一样的。Linux的输出重定向使用也要类似的这样两个步骤。如果你自己看他的代码，可以看到创建的是一个只读的、不支持异步的FileSteam来的，他代码是这样写的：</strong></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;Start()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;ProcessStartInfo&nbsp;startInfo&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.StartInfo;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(startInfo.FileName.Length&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;InvalidOperationException(SR.GetString(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">FileNameMissing</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(startInfo.UseShellExecute)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.StartWithShellExecuteEx(startInfo);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.StartWithCreateProcess(startInfo);<br />}<br /><br /></span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;StartWithCreateProcess(ProcessStartInfo&nbsp;startInfo)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;((startInfo.StandardOutputEncoding&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">startInfo.RedirectStandardOutput)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;InvalidOperationException(SR.GetString(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">StandardOutputEncodingNotAllowed</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;((startInfo.StandardErrorEncoding&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">startInfo.RedirectStandardError)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;InvalidOperationException(SR.GetString(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">StandardErrorEncodingNotAllowed</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.disposed)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ObjectDisposedException(</span><span style="color: #0000FF; ">base</span><span style="color: #000000; ">.GetType().Name);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;cmdLine&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;BuildCommandLine(startInfo.FileName,&nbsp;startInfo.Arguments);<br />&nbsp;&nbsp;&nbsp;&nbsp;NativeMethods.STARTUPINFO&nbsp;lpStartupInfo&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;NativeMethods.STARTUPINFO();<br />&nbsp;&nbsp;&nbsp;&nbsp;SafeNativeMethods.PROCESS_INFORMATION&nbsp;lpProcessInformation&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SafeNativeMethods.PROCESS_INFORMATION();<br />&nbsp;&nbsp;&nbsp;&nbsp;SafeProcessHandle&nbsp;processHandle&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SafeProcessHandle();<br />&nbsp;&nbsp;&nbsp;&nbsp;SafeThreadHandle&nbsp;handle2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SafeThreadHandle();<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;error&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;SafeFileHandle&nbsp;parentHandle&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;SafeFileHandle&nbsp;handle4&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;SafeFileHandle&nbsp;handle5&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;GCHandle&nbsp;handle6&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;GCHandle();<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;flag;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;((startInfo.RedirectStandardInput&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;startInfo.RedirectStandardOutput)&nbsp;</span><span style="color: #000000; ">||</span><span style="color: #000000; ">&nbsp;startInfo.RedirectStandardError)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(startInfo.RedirectStandardInput)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.CreatePipe(</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">&nbsp;parentHandle,&nbsp;</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">&nbsp;lpStartupInfo.hStdInput,&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpStartupInfo.hStdInput&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SafeFileHandle(NativeMethods.GetStdHandle(</span><span style="color: #000000; ">-</span><span style="color: #000000; ">10</span><span style="color: #000000; ">),&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(startInfo.RedirectStandardOutput)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.CreatePipe(</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">&nbsp;handle4,&nbsp;</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">&nbsp;lpStartupInfo.hStdOutput,&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpStartupInfo.hStdOutput&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SafeFileHandle(NativeMethods.GetStdHandle(</span><span style="color: #000000; ">-</span><span style="color: #000000; ">11</span><span style="color: #000000; ">),&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; "><br />中间省略一部分</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #454545; font-family: 'Microsoft Yahei', 微软雅黑, Tahoma, Arial, Helvetica, STHeiti; line-height: 25px; background-color: #ffffff; ">&nbsp;&nbsp;<span style="background-color: #eeeeee; font-size: 13px; ">&nbsp;</span><span style="background-color: #eeeeee; font-size: 13px; color: #0000ff; ">if</span><span style="background-color: #eeeeee; font-size: 13px; ">&nbsp;(startInfo.RedirectStandardInput)</span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all">&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.standardInput&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;StreamWriter(<span style="color: #0000FF; ">new</span>&nbsp;FileStream(parentHandle,&nbsp;FileAccess.Write,&nbsp;0x1000,&nbsp;<span style="color: #0000FF; ">false</span>),&nbsp;Encoding.GetEncoding(NativeMethods.GetConsoleCP()),&nbsp;0x1000);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.standardInput.AutoFlush&nbsp;=&nbsp;<span style="color: #0000FF; ">true</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(startInfo.RedirectStandardOutput)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Encoding&nbsp;encoding&nbsp;=&nbsp;(startInfo.StandardOutputEncoding&nbsp;!=&nbsp;<span style="color: #0000FF; ">null</span>)&nbsp;?&nbsp;startInfo.StandardOutputEncoding&nbsp;:&nbsp;Encoding.GetEncoding(NativeMethods.GetConsoleOutputCP());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.standardOutput&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;StreamReader(<span style="color: #0000FF; ">new</span>&nbsp;FileStream(handle4,&nbsp;FileAccess.Read,&nbsp;0x1000,&nbsp;<span style="color: #0000FF; ">false</span>),&nbsp;encoding,&nbsp;<span style="color: #0000FF; ">true</span>,&nbsp;0x1000);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(startInfo.RedirectStandardError)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Encoding&nbsp;encoding2&nbsp;=&nbsp;(startInfo.StandardErrorEncoding&nbsp;!=&nbsp;<span style="color: #0000FF; ">null</span>)&nbsp;?&nbsp;startInfo.StandardErrorEncoding&nbsp;:&nbsp;Encoding.GetEncoding(NativeMethods.GetConsoleOutputCP());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.standardError&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;StreamReader(<span style="color: #0000FF; ">new</span>&nbsp;FileStream(handle5,&nbsp;FileAccess.Read,&nbsp;0x1000,&nbsp;<span style="color: #0000FF; ">false</span>),&nbsp;encoding2,&nbsp;<span style="color: #0000FF; ">true</span>,&nbsp;0x1000);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">bool</span>&nbsp;flag3&nbsp;=&nbsp;<span style="color: #0000FF; ">false</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(!processHandle.IsInvalid)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.SetProcessHandle(processHandle);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.SetProcessId(lpProcessInformation.dwProcessId);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handle2.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag3&nbsp;=&nbsp;<span style="color: #0000FF; ">true</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;flag3;<br />}<br /><br /><span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;CreatePipe(<span style="color: #0000FF; ">out</span>&nbsp;SafeFileHandle&nbsp;parentHandle,&nbsp;<span style="color: #0000FF; ">out</span>&nbsp;SafeFileHandle&nbsp;childHandle,&nbsp;<span style="color: #0000FF; ">bool</span>&nbsp;parentInputs)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;NativeMethods.SECURITY_ATTRIBUTES&nbsp;lpPipeAttributes&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;NativeMethods.SECURITY_ATTRIBUTES();<br />&nbsp;&nbsp;&nbsp;&nbsp;lpPipeAttributes.bInheritHandle&nbsp;=&nbsp;<span style="color: #0000FF; ">true</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;SafeFileHandle&nbsp;hWritePipe&nbsp;=&nbsp;<span style="color: #0000FF; ">null</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(parentInputs)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CreatePipeWithSecurityAttributes(<span style="color: #0000FF; ">out</span>&nbsp;childHandle,&nbsp;<span style="color: #0000FF; ">out</span>&nbsp;hWritePipe,&nbsp;lpPipeAttributes,&nbsp;0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CreatePipeWithSecurityAttributes(<span style="color: #0000FF; ">out</span>&nbsp;hWritePipe,&nbsp;<span style="color: #0000FF; ">out</span>&nbsp;childHandle,&nbsp;lpPipeAttributes,&nbsp;0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(!NativeMethods.DuplicateHandle(<span style="color: #0000FF; ">new</span>&nbsp;HandleRef(<span style="color: #0000FF; ">this</span>,&nbsp;NativeMethods.GetCurrentProcess()),&nbsp;hWritePipe,&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;HandleRef(<span style="color: #0000FF; ">this</span>,&nbsp;NativeMethods.GetCurrentProcess()),&nbsp;<span style="color: #0000FF; ">out</span>&nbsp;parentHandle,&nbsp;0,&nbsp;<span style="color: #0000FF; ">false</span>,&nbsp;2))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;Win32Exception();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">finally</span><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;((hWritePipe&nbsp;!=&nbsp;<span style="color: #0000FF; ">null</span>)&nbsp;&amp;&amp;&nbsp;!hWritePipe.IsInvalid)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hWritePipe.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br /><span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;CreatePipeWithSecurityAttributes(<span style="color: #0000FF; ">out</span>&nbsp;SafeFileHandle&nbsp;hReadPipe,&nbsp;<span style="color: #0000FF; ">out</span>&nbsp;SafeFileHandle&nbsp;hWritePipe,&nbsp;NativeMethods.SECURITY_ATTRIBUTES&nbsp;lpPipeAttributes,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;nSize)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;((!NativeMethods.CreatePipe(<span style="color: #0000FF; ">out</span>&nbsp;hReadPipe,&nbsp;<span style="color: #0000FF; ">out</span>&nbsp;hWritePipe,&nbsp;lpPipeAttributes,&nbsp;nSize)&nbsp;||&nbsp;hReadPipe.IsInvalid)&nbsp;||&nbsp;hWritePipe.IsInvalid)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;Win32Exception();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br /><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">override</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;Peek()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(<span style="color: #0000FF; ">this</span>.stream&nbsp;==&nbsp;<span style="color: #0000FF; ">null</span>)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__Error.ReaderClosed();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;((<span style="color: #0000FF; ">this</span>.charPos&nbsp;!=&nbsp;<span style="color: #0000FF; ">this</span>.charLen)&nbsp;||&nbsp;(!<span style="color: #0000FF; ">this</span>._isBlocked&nbsp;&amp;&amp;&nbsp;(<span style="color: #0000FF; ">this</span>.ReadBuffer()&nbsp;!=&nbsp;0)))<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">this</span>.charBuffer[<span style="color: #0000FF; ">this</span>.charPos];<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;-1;<br />}</div>转自：<a href="http://hi.baidu.com/widebright/item/f58e2516a6bb41dcbf9042a4">http://hi.baidu.com/widebright/item/f58e2516a6bb41dcbf9042a4</a><img src ="http://www.cppblog.com/flyinghare/aggbug/187371.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2012-08-16 13:30 <a href="http://www.cppblog.com/flyinghare/archive/2012/08/16/187371.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于系统中未处理异常的全局捕获</title><link>http://www.cppblog.com/flyinghare/archive/2012/05/30/176726.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 30 May 2012 03:13:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2012/05/30/176726.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/176726.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2012/05/30/176726.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/176726.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/176726.html</trackback:ping><description><![CDATA[<p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; color: #464646; font-family: simsun; text-align: left; background-color: #e5e7e7; "><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;前两天有朋友问：&#8220;如何监控自己的程序创建的子进程所发生的异常？&#8221;一开始，我以为这子进程是他自己编写的程序，所以直接就说：&#8220;在子进程的代码中使用&#8220;<a href="http://msdn.microsoft.com/en-us/library/ms680634(v=vs.85).aspx" style="text-decoration: none; color: #286446; "><em>SetUnhandledExceptionFil<wbr>ter</em></a>&#8221;这个API，注册一个未处理异常的回调函数，然后在回调函数中做相应处理就可以了。&#8221;实际上，现在大部分应用程序也都使用了这个API来监控自己的程序的崩溃情况。例如QQ、遨游等，在崩溃的时候都会提示需要发送一个错误报告文件，这个错误报告文件就是用这种方式生成的。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;但是，随后这位朋友说，这个子进程并不是他编写的，没办法修改程序的代码。实际上他是需要做计算机病毒分析，要对一些病毒程序的运行情况进行监控。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;在这样的状况下，我想到了这样一个思路：就是将他的监控程序注册为系统中的实时调试器，这样系统中有任何进程发生崩溃时，Windows都会将该进程的PID传递给这个监控程序。至于崩溃进程是否是他的子进程，只需要根据PID就可以简单地判断出来了。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;所谓&#8220;实时调试&#8221;（英语叫<em>Just-In-Time debugging</em>），其实就是Visual Studio以及Windbg等调试程序将自己注册为系统中的默认调试器的机制。用过Visual Studio的都知道，安装了这玩意之后，每当程序崩溃，VS都可以直接跳出来询问是否对该进程进行调试，这就是&#8220;实时调试&#8221;的能力。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;具体的注册方式其实很简单，修改注册表就可以实现。打开[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]这个键，在其下建立&#8220;Debugger&#8221;的字符串值，具体内容填写&#8220;监控进程的全路径 -p %ld -e %ld&#8221;，例如&#8220;c:\exceptionMonitor.exe -p %ld -e %ld&#8221;。监控程序要支持两个参数，一个是&#8220;-p&#8221;，一个是&#8220;-e&#8221;，当有程序崩溃时，系统会把用程序的PID替换参数中的&#8220;%ld&#8221;，传递给监控程序。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;但还有一些其他的问题，这就是系统只能设置一个实时调试器。如果系统中已经有VS等程序注册过了，我们去修改这个值不就影响了这些程序的监控吗？这个问题其实也好解决，只要采取计算机安全领域里用到烂大街的&#8220;Hook&#8221;思想就可以了。具体方式是首先将VS等其他程序注册的&#8220;Debugger&#8221;值保存到另外一个键值下，例如取名为&#8220;PreviousDebugger&#8221;，然后再把我们的程序写到真正的&#8220;Debugger&#8221;键值下。当系统中的程序发生崩溃并被我们的监控程序截获后，我们首先进行自己的处理，处理完毕再根据&#8220;PreviousDebugger&#8221;中记录的调试程序路径，将崩溃的程序&#8220;扔&#8221;给后续的调试程序。如此一来，大家都有机会进行处理，各取所需，皆大欢喜了。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; word-wrap: normal; word-break: normal; ">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;最后要说明的是，在[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]下面还有一个Auto值，如果这个值为&#8220;0&#8221;的话，那么当程序崩溃的时候系统会弹出一个对话框，让用户选择是否启动调试器。要是用户选择不启动，我们的监控程序就等于是&#8220;白给&#8221;了。所以，我们务必要把这个值设为&#8220;1&#8221;啊，切记切记。</p><br />转自：<a href="http://blog.sina.com.cn/s/blog_53f909390100n3p8.html">http://blog.sina.com.cn/s/blog_53f909390100n3p8.html</a></p><img src ="http://www.cppblog.com/flyinghare/aggbug/176726.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2012-05-30 11:13 <a href="http://www.cppblog.com/flyinghare/archive/2012/05/30/176726.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>跑步的好处</title><link>http://www.cppblog.com/flyinghare/archive/2012/05/15/174954.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Tue, 15 May 2012 03:36:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2012/05/15/174954.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/174954.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2012/05/15/174954.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/174954.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/174954.html</trackback:ping><description><![CDATA[<span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">1.告别臃肿身材。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">许多人开始跑步就是因为减肥，跑步确实减肥的最好运动方式，跑步每分钟比起其他运动燃烧更多的卡路里。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">2.防止你的骨骼，肌肉退化。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">我们的骨骼是和你的身体需求相互协调的。长期坐在显示器前的我们让我们的骨骼越来越脆弱。而长期的，经常的运动会使你的骨骼保持健康。更进一步说就是防止我们身体内部老化的更快。经常的高强度锻炼，例如跑步，被证明可以促进人体荷尔蒙的生长，荷尔蒙就是那些名人为了看起来更年轻而持续注射的药剂。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">3.抵抗疾病&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">跑步可以降低得中风和乳腺癌的风险。经常的跑步已经成为医生对那些容易引发或在已经处在早期的骨质疏松，糖尿病，高血压病人的治疗建议。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">4.维持并提高总体的身体水平。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">跑步是是人们可以采取的最好的锻炼身体的运动。它可以提高胆固醇,降低血液凝块的危险，锻炼你的50%的经常处于闲置状态的肺。跑步还可以通过增加你的淋巴细胞来增强你的免疫力。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">5.让你更加自信。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">慢跑像其他一些单人运动一样，它可以增强你的自信心。跑步让你完成一次又一次的尝试，让你变得更强大，更加肯定自己。他让你真实的越过某个山峰，穿过某个障碍.在意识到你的身体已经更加强壮，更加有用，你会得到被赋予力量和自由的感觉。自信更是那些通过跑步成功的减肥并得到自己心中理想身材的跑步者的宝贵财富。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">6.放松自己，减轻压力。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">慢跑可以转移聂注意力，沐浴在路旁的风景中，你的烦恼一定会消失殆尽。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">长跑适合那些正处在一堆头疼，恼人的烦心事的人。还有比在两个小时的长跑中，清理的的头脑、舒缓自己的神经更好的主意了吗。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">如果你此时觉得异常压抑，何不快跑一下呢，之后你会一个好的心情。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">7.著名的&#8220;跑步者高峰体验&#8221;&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">包括释放压力，慢跑被证明提高你的心态。跑步，特别在户外和旅行中,会使身体释放一种物质让你产生一种幸福愉悦感（跑步者高峰体验）或者就是快乐的感觉。跑步已经被采用了多年来治疗临床抑郁症，上瘾等。更少的压力，更少的压抑，更少的疲劳，更少的混乱，经过一段时间的经常跑步，病人很快就有了变化。跑步让他们有了注意的对象，让他们看到了除了他们消极的状态和沉溺的事务，还有一些美好的东西的存在。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">8.锻炼你的头脑。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">像对你的身体有所帮助一样，跑步同样对你的头脑很有帮助。通过在跑步中克服一系列的障碍，你学会了专注和决心.在经历那些你几乎要放弃的长跑或其他项目后你会发现：你在跑步过程中产生的意志和体魄的增强让你在其他方面有着同样的专注和决心。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">9.增强合作精神。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">又是一个非常值得去做的好处。这点好处或许让很多人感到惊奇，因为人们认为跑步不可能得到这种益处，仅仅由于跑步是单人运动。但是跑步确实有时涉及到互相合作。旅行跑步，特别是在那些路况不好的地方，需要极大的合作意识。这些路面经常会有一些 障碍如石头、灌木让跑步进行的很困难。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">10.随时随地，简单。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">不是很多的运动可以在任何地方，几乎不需要设备的。我敢肯定古代希腊人会争辩说甚至是鞋子和衣服也不需要。今天，我们只是需要一双好点的跑步鞋然后就可以出发了。从市中心到郊区,整个世界的地方等待你的探索。经常出差吗？你的旅行箱里肯定会有空间来装你的运动鞋的。这个世界就是你的健身房，去再次发现它吧。&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">Here are some tips for how to make running a practice:&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">Be consistent in your running program. Plan your weekly workout schedule and stick to it. This will teach you persistence.&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">Know which focuses you'll use during every run. This will teach you planning and mindfulness, and improve your mind/body connection.&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">Constantly practice relaxing your muscles. This will help to relieve tension and train you to relax no matter what activity you're doing.&nbsp;</span><br style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; " /><span style="color: #111111; font-family: Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; ">At the end of your run, spend a few minutes doing an "end-of-run review." Ask yourself how well you did with keeping your focuses, how your body felt during the run. What did you come away with that will help your next run? Then, the next time you go out for a run, you'll have something to work on that you brought forward from your last run. In this way you'll build a healthy, growing and sustainable running program.&nbsp;</span>&nbsp;<br />转自：<a href="http://www.douban.com/group/topic/20749798/">http://www.douban.com/group/topic/20749798/</a><img src ="http://www.cppblog.com/flyinghare/aggbug/174954.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2012-05-15 11:36 <a href="http://www.cppblog.com/flyinghare/archive/2012/05/15/174954.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 RDSTC 获得精确时间</title><link>http://www.cppblog.com/flyinghare/archive/2012/02/22/166245.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 22 Feb 2012 08:08:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2012/02/22/166245.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/166245.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2012/02/22/166245.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/166245.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/166245.html</trackback:ping><description><![CDATA[<div style="clear: both; line-height: 20px; padding-bottom: 10px; color: #464646; font-family: Verdana, 宋体, sans-serif; font-size: 12px; text-align: left; background-color: #f8ecd8; "><h2>获得程序或者一段代码运行的时间</h2>&nbsp;<span sg_txtc"="" style="color: #909090; white-space: nowrap; font-family: Arial; font-size: 10px; margin-left: 5px; margin-right: 13px; ">(2006-06-14 09:49:11)</span><div style="float: right; "><a href="http://blog.sina.com.cn/s/blog_49cd05f9010004sy.html" sg_abtn_ico=""  sg_turn"="" action-type="reblog" action-data="{srcBlog:1, blogId:'49cd05f9010004sy'}" style="text-decoration: none; color: #464646; cursor: pointer; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 3px; overflow-x: hidden; overflow-y: hidden; white-space: nowrap; margin-right: 6px; background-image: url(http://simg.sinajs.cn/blog7newtpl/image/1/1_2/images/sg_newsp.png); position: relative; letter-spacing: 5px; width: 86px; zoom: 1; display: inline-block; background-position: 0px 0px; background-repeat: no-repeat no-repeat; "><cite style="font-style: normal; line-height: 23px; padding-top: 0px; padding-right: 20px; padding-bottom: 0px; padding-left: 32px; height: 23px; min-width: 1px; overflow-x: visible; background-image: url(http://simg.sinajs.cn/blog7newtpl/image/1/1_2/images/sg_newsp.png); width: 34px; display: inline-block !important; background-position: 100% -28px; background-repeat: no-repeat no-repeat; "><img sg_icon111"="" src="http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif" width="15" height="15" align="absmiddle" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-image: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; background-image: url(http://simg.sinajs.cn/blog7style/images/common/sg_icon.png); position: absolute; left: 11px; top: 4px; background-position: 495px -60px; "  alt="" />转载<em style="font-style: normal; -webkit-text-size-adjust: none; font-size: 7px; position: absolute; right: 5px; top: 2px; ">&#9660;</em></cite></a></div></div><div id="sina_keyword_ad_area" style="width: 690px; clear: both; word-break: break-all; line-height: 20px; color: #464646; font-family: Verdana, 宋体, sans-serif; font-size: 12px; text-align: left; background-color: #f8ecd8; "><table style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><tbody><tr><td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 10px; padding-bottom: 0px; padding-left: 0px; font-size: 12px; font-family: 宋体; vertical-align: top; "></td><td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 12px; font-family: 宋体; vertical-align: top; width: 220px; white-space: nowrap; "></td></tr></tbody></table></div><div id="sina_keyword_ad_area2"  "="" style="width: 690px; clear: both; padding-top: 18px; padding-bottom: 30px; word-wrap: normal; word-break: normal; overflow-x: hidden; overflow-y: hidden; font-family: simsun; color: #464646; text-align: left; background-color: #f8ecd8; "><div>如何获得程序或者一段代码运行的时间？你可能说有专门的程序测试工具，确实，不过你也可以在程序中嵌入汇编代码来实现。</div><div>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;在Pentium的指令系统中有一条指令可以获得CPU内部64位计数器的值，我们可以通过代码两次获取该计数器的值而获得程序或代码运行的时钟周期数，进而通过你的cpu的频率算出一个时钟周期的时间，从而算出程序运行的确切时间。</div><div>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;我们通过指令TDSIC来获得cpu内部计数器的值,指令TDSIC返回值放在EDX:EAX中,其中EDX中存放64位寄存器中高32位的值,EAX存放第32位的值.</div><div>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;下面看看实现的代码:</div><div>&nbsp;<wbr></div><div>//用汇编实现获取一段代码运行的时间<br />#include&lt;iostream&gt;</div><div>using namespace std;</div><div>void GetClockNumber (long high, long low);<br />void GetRunTime();</div><div>int main()<br />{&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></div><div>long HighStart,LowStart,HighEnd,LowEnd;<br />long numhigh,numlow;<br />//获取代码运行开始时cpu内部计数器的值<br />__asm&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br />{<br />&nbsp;<wbr>&nbsp;RDTSC<br />&nbsp;<wbr>&nbsp;mov HighStart, edx<br />&nbsp;<wbr>&nbsp;mov LowStart, eax<br />}<br />for(int i= 0; i&lt;100000; i++ )<br />{<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;for(int i= 0; i&lt;100000; i++ )<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;{<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;}<br />}</div><div>&nbsp;<wbr></div><div>&nbsp;<wbr></div><div>&nbsp;<wbr></div><div><br />&nbsp;<wbr>&nbsp;//获取代码结束时cpu内部计数器的值，并减去初值<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;__asm<br />{<br />&nbsp;<wbr>&nbsp;RDTSC<br />&nbsp;<wbr>&nbsp;mov HighEnd, edx<br />&nbsp;<wbr>&nbsp;Mov LowEnd,&nbsp;<wbr>&nbsp;eax<br />&nbsp;<wbr>&nbsp;;获取两次计数器值得差<br />&nbsp;<wbr>&nbsp;sub eax,&nbsp;<wbr>&nbsp;LowStart<br />&nbsp;<wbr>&nbsp;cmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;eax,&nbsp;<wbr>&nbsp;0&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;; 如果低32的差为负则求返,因为第二次取得永远比第一次的大<br />&nbsp;<wbr>&nbsp;jg&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;L1<br />&nbsp;<wbr>&nbsp;neg&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;eax<br />&nbsp;<wbr>&nbsp;jmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;L2<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;L1: mov numlow,&nbsp;<wbr>&nbsp;eax<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;L2: sbb edx,&nbsp;<wbr>&nbsp;HighStart<br />&nbsp;<wbr>&nbsp;mov numhigh, edx<br />&nbsp;<wbr><br />}<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;//把两个计数器值之差放在一个64位的整形变量中<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;//先把高32位左移32位放在64的整形变量中,然后再加上低32位<br />__int64&nbsp;<wbr>&nbsp;timer =(numhigh&lt;&lt;32) + numlow;<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;//输出代码段运行的时钟周期数<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;//以频率1.1Gcpu为例,如果换计算机把其中的1.1改乘其它即可,因为相信大家的cpu都应该在1G以上&nbsp;<wbr>&nbsp;^_^<br />cout&lt;&lt; (double) (timer /1.1/1000000000) &lt;&lt; endl;<br />return 0;<br />}</div><div><br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;这样通过一条简单的汇编指令就可以获得程序或一段代码的大概时间，不过并不能得到运行的确切时间，因为即使去掉中间的循环，程序也会有个运行时间，</div><div>因为在第一次取得计数器的值后，有两条汇编指令mov HighStart, edx&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;mov LowStart, eax这两条指令当然也有运行时间&nbsp;，当然你可以减去这两条指令的运行时间(在1.1G的机子上是3e-8s)，这样会更精确一点。</div><div>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;如果你要确切知道程序的运行时间，专业的测试软件肯定会更好一点，不过好像一般没有必要获取除非专门的要求的程序。</div><div>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;不过能DIY一个也是不错的，不管有没有，最起码你可以学到在VC++中如何嵌入汇编代码以及如何使用32位的寄存器，其实和16位的寄存器一样使用，将来64的也应该一样，只不过位数不同罢了。</div><div>&nbsp;<br />转自：<a href="http://blog.sina.com.cn/s/blog_49cd05f9010004sy.html">http://blog.sina.com.cn/s/blog_49cd05f9010004sy.html<br /><br /></a></div></div><img src ="http://www.cppblog.com/flyinghare/aggbug/166245.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2012-02-22 16:08 <a href="http://www.cppblog.com/flyinghare/archive/2012/02/22/166245.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DOM应用---遍历网页中的元素</title><link>http://www.cppblog.com/flyinghare/archive/2011/09/21/156481.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 21 Sep 2011 15:09:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2011/09/21/156481.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/156481.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2011/09/21/156481.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/156481.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/156481.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="font-family: Tahoma; letter-spacing: 1px; line-height: 24px; background-color: #ffffff; "><p align="center">作者：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#103;&#111;&#111;&#100;&#95;&#121;&#102;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="font-size: 14px; color: #000088; text-decoration: none; font-family: 宋体; ">杨老师</a></p><p><a href="http://www.vckbase.com/code/downcode.asp?id=2666" style="font-size: 14px; color: #000088; text-decoration: none; font-family: 宋体; ">下载源代码</a><br /><br /><strong>一、摘要</strong><br />　　在我们编写的程序中，如果想要实现对浏览器打开的网页进行监视、模拟操纵、动态提取用户输入、动态修改......等功能，那么请你抽出宝贵的时间，继续往下阅读。本文介绍的知识和示例程序都是围绕如何遍历 HTML 中的表单(form)并枚举出表单域的属性为目标的，对于网页中的其它元素，比如图象、连接、脚本等等，应用同样的方法都可以轻松实现。<br /><br /><strong>二、网页的文档层次结构</strong><br />　　IE 浏览器，采用 DOM（文档对象模型）来管理网页的数据。它通过一个容器（IWebBrowser2/IHTMLWindow2）来装载网页文档（IHTMLDocument2），而一个文档，又可以由 0 或多个贞(frame)组成，管理这些贞的接口叫&#8220;框架集合（IHTMLFramesCollection2）&#8221;,而每个贞的容器又是IHTMLWindow2，和IWebBrowser2一样，它也装载着各自的文档（IHTMLDocument2）。因此，我们的第一个任务，就是想方设法能够得到IHTMLDocument2的接口。因为文档可能包含贞，而贞又包含着子文档，子文档可能再包含贞......，如此要得到所有的文档，这里有一个递归遍历的处理过程。<br />　　得到文档（IHTMLDocument2）后，下一步任务就是要设法取得表单了（IHTMLFormElement）。因为在一个文档中可以包含 0 或多个表单(form)，而管理这些表单的又是一个表单集合（IHTMLElementCollection），所以必须先得到集合，然后再枚举出所有的表单条目了。<br />　　得到表单（IHTMLFormElement）后，接下来的事情就简单了，逐个提取表单中的元素（也叫表单域 IHTMLInputElement）就可以读写这些域的属性了。<br />　　说了半天，我估计初次接触的朋友一定没有听懂:( 呵呵，还是用图的方式表示一下吧，这样比较清晰一些。<br />&nbsp;<img src="http://www.cppblog.com/images/cppblog_com/flyinghare/EnumHtmlEnumimg.jpg" alt="" /></p><p><strong>三、程序实现</strong><br /><br />&lt;1&gt; 取得 IHTMLDocument2 的接口指针。根据IE浏览器的运行方式，有多种不同的方式可以获取文档指针。<br />&nbsp; &lt;1.1&gt; 如果你在程序中使用MFC的 CHtmlView 视来浏览网页。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得文档的方法最简单，调用 CHtmlView::GetHtmlDocument() 函数。<br />&nbsp; &lt;1.2&gt; 如果你的程序中使用了&#8220;Web 浏览器&#8221; 的ActiveX 控件。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得文档的方法也比较简单，调用 CWebBrowser2::GetDocument() 函数。<br />&nbsp; &lt;1.3&gt; 如果你的程序是用 ATL 写的 ActiveX 控件。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么需要调用 IOleClientSite::GetContainer 得到 IOleContainer 接口，然后就可以通过 QueryInterface() 查询得到 IHTMLDocument2 的接口。主要代码如下：&nbsp;<br /></p><pre>CComPtr &lt; IOleContainer &gt; spContainer;
m_spClientSite-&gt;GetContainer( &amp;spContainer );
CComQIPtr &lt; IHTMLDocument2 &gt; spDoc = spContainer;
if ( spDoc )
{
     // 已经得到了 IHTMLDocument2 的接口指针
}
</pre>&nbsp; &lt;1.4&gt; 如果你的程序是用 MFC 写的 ActiveX 控件。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么需要调用 COleControl::GetClientSite() 得到 IOleContainer 接口，然后的操作和&lt;1.3&gt;是一致的了。<br />&nbsp; &lt;1.5&gt; IE 浏览器作为独立的进程正在运行。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个运行的浏览器（IE 和 资源浏览器）都会在 ShellWindows 中进行登记，因此我们要通过 IShellWindows 取得实例（示例程序中使用的就是这个方法）。主要代码如下：<br /><pre>#include &lt; atlbase.h &gt;
#include &lt; mshtml.h &gt;

void FindFromShell() 
{
	CComPtr&lt; IShellWindows &gt; spShellWin;
	HRESULT hr = spShellWin.CoCreateInstance( CLSID_ShellWindows );
	if ( FAILED( hr ) )    return;

	long nCount=0;
	spShellWin-&gt;get_Count(&amp;nCount);   // 取得浏览器实例个数

	for(long i=0; i&lt;nCount; i++)
       {
              CComPtr&lt; IDispatch &gt;&lt;nCount; i++)
	{
		CComPtr&lt; IDispatch &gt;&lt;nCount; i++)
       {
              CComPtr&lt; IDispatch &gt; spDisp;
		hr=spShellWin-&gt;Item(CComVariant( i ), &amp;spDisp );
		if ( FAILED( hr ) )   continue;

		CComQIPtr&lt; IWebBrowser2 &gt; spBrowser = spDisp;
		if ( !spBrowser )     continue;

		spDisp.Release();
		hr = spBrowser-&gt;get_Document( &amp;spDisp );
		if ( FAILED ( hr ) )  continue;

		CComQIPtr&lt; IHTMLDocument2 &gt; spDoc = spDisp;
		if ( !spDoc )         continue;

		// 程序运行到此，已经找到了 IHTMLDocument2 的接口指针
	}
}
</pre><p>&nbsp; &lt;1.6&gt; IE 浏览器控件被一个进程包装在一个子窗口中。那么你首先要得到那个进程的顶层窗口句柄（使用 FindWindow() 函数，或其它任何可行的方法），然后枚举所有子窗口，通过判断窗口类名是否是&#8220;Internet Explorer_Server&#8221;，从而得到浏览器的窗口句柄，再向窗口发消息取得文档的接口指针。主要代码如下：&nbsp;<br /></p><pre>#include &lt; atlbase.h &gt;
#include &lt; mshtml.h &gt;
#include &lt; oleacc.h &gt;
#pragma comment ( lib, "oleacc" )

BOOL CALLBACK EnumChildProc(HWND hwnd,LPARAM lParam)
{
	TCHAR szClassName[100];

	::GetClassName( hwnd,  &amp;szClassName,  sizeof(szClassName) );
	if ( _tcscmp( szClassName,  _T("Internet Explorer_Server") ) == 0 )
	{
		*(HWND*)lParam = hwnd;
		return FALSE;		// 找到第一个 IE 控件的子窗口就停止
	}
	else	return TRUE;		// 继续枚举子窗口
};

void FindFromHwnd(HWND hWnd) 
{
	HWND hWndChild=NULL;
	::EnumChildWindows( hWnd, EnumChildProc, (LPARAM)&amp;hWndChild );
	if(NULL == hWndChild)	return;

	UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") );
	LRESULT lRes;
	::SendMessageTimeout( hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*) &amp;lRes );

	CComPtr &lt; IHTMLDocument2 &gt; spDoc;
	HRESULT hr = ::ObjectFromLresult ( lRes, IID_IHTMLDocument2, 0 , (LPVOID *) &amp;spDoc );
	if ( FAILED ( hr ) )	return;

	// 程序运行到此，已经找到了 IHTMLDocument2 的接口指针
}
</pre>&lt;2&gt; 得到了 IHTMLDocument2 接口指针后，如果网页是单贞的，那么转第&lt;4&gt;步骤。如果是多贞（有子框架）则还需要遍历所有的子框架。这些子框架（IHTMLWindow2），被保存在集合中（IHTMLFramesCollection2）,取得集合指针的方法比较简单，取属性 IHTMLDocument2::get_frames()。<br />&lt;3&gt; 首先取得子框架的总数目 IHTMLFramesCollection::get_length()，接着就可以循环调用 IHTMLFramesCollection::item()函数一个一个地取得子框架 IHTMLWindow2 指针，然后转第&lt;1&gt;步。<br />&lt;4&gt; 一个文档中可能拥有多个表单，因此还是同样的道理，先要取得表单的集合（IHTMLElementCollection，其实这个不光是表单的集合，其他元素的集合，比如图片集合也是用它）。这个操作也很简单，取得属性 IHTMLDocument2::get_forms()。<br />&lt;5&gt; 属性 IHTMLElementCollection::get_length() 得到表单总数目，就可以循环取得每一个表单指针了 IHTMLElementCollection::item()。<br />&lt;6&gt; 在第&lt;5&gt;步中的item()函数，得到的是一个IDispatch的指针，你通过QueryInterface()查询，就可以得到 某类型输入的指针，代码如下：<pre>// 假设 spDisp 是由IHTMLElementCollection::item() 得到的 IDispatch 指针
CComQIPtr &lt; IHTMLInputTextElement &gt;     spInputText(spDisp);
CComQIPtr &lt; IHTMLInputButtonElement &gt;   spInputButton(spDisp);
CComQIPtr &lt; IHTMLInputHiddenElement &gt;   spInputHidden(spDisp);
......
if ( spInputText )
{
   //如果是文本输入表单域
}
else if ( spInputButton )
{
   //如果是按纽输入表单域
}
else if ( spInputHiddent )
{
   //如果是隐藏输入表单域
}
else if ........    //其它输入类型
</pre>　　上面的方法，由于使用具体类型的接口指针，因此程序的效率比较高。但是通过 QueryInterface 接口查询，然后再进行条件判断显然是比较烦琐的，所以这个方法适合于特定的已知网页设计内容的程序。在示例程序中，我则是直接使用 IDispatch 接口进行操作的，这个方式执行起来稍微慢一些，但程序比较简单。主要代码和说明如下：<pre>#include &lt; atlbase.h &gt;
CComModule  _Module;	// 由于需要使用 CComDispatchDriver 的 IDispatch 包装类ATL智能指针，所以这个是必须的
#include &lt; atlcom.h &gt;
......
long nElemCount=0;		//表单域的总数目
spFormElement-&gt;get_length( &amp;nElemCount );

for(long j=0; j&lt; nElemCount; j++)
{
	CComDispatchDriver spInputElement;	// IDispatch 的智能指针
	spFormElement-&gt;item( CComVariant( j ), CComVariant(), &amp;spInputElement );

	CComVariant vName,vVal,vType;	// 域名称，域值，域类型
	spInputElement.GetPropertyByName( L"name", &amp;vName );
	spInputElement.GetPropertyByName( L"value",&amp;vVal  );
	spInputElement.GetPropertyByName( L"type", &amp;vType );
	// 使用 IDispatch 的智能指针的好处就是：象上面这样读取、设置属性很简单
	// 另外调用 Invoke 函数也异常方便，Invoke0(),Invoke1(),Invoke2()....
	......
}
</pre><strong>四、结束语</strong><br />　　示例程序在 VC6 下编译执行通过。运行方法：随便启动几个 IE 浏览网页，最好是有表单输入的网页。然后执行示例的 EXE 程序即可。到这里，就到这里了......祝大家学习快乐 ^-^<br /><br /><br /></span>转自：<a href="http://www.vckbase.com/document/viewdoc/?id=1446">http://www.vckbase.com/document/viewdoc/?id=1446</a><img src ="http://www.cppblog.com/flyinghare/aggbug/156481.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2011-09-21 23:09 <a href="http://www.cppblog.com/flyinghare/archive/2011/09/21/156481.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MS Active Accessibility 接口技术编程尝试</title><link>http://www.cppblog.com/flyinghare/archive/2011/09/21/156477.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 21 Sep 2011 14:33:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2011/09/21/156477.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/156477.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2011/09/21/156477.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/156477.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/156477.html</trackback:ping><description><![CDATA[<div><p align="center"></p> <p><br /><a href="http://www.vckbase.com/code/downcode.asp?id=2140">下载源代码</a> <br /></p><pre>Microsoft&#169; Active Accessibility 2.0 is a COM-based technology that improves the  way accessibility aids work with applications running on Microsoft Windows?. It  provides dynamic-link libraries that are incorporated into the operating system  as well as a COM interface and application programming elements that provide  reliable methods for exposing information about user interface elements.      </pre><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" /> 基础<br /> Microsoft&#169; Active Accessibility  是一种相对较新的技术（1.0版在1997年5月份推出）。目的是方便身患残疾的人士使用电脑&#8212;&#8212;可用于放大器、屏幕阅读器，以及触觉型鼠标。同样还可以用来开发驱动其它软件的应用程序，其模拟用户输入的能力尤其适合测试软件的开发。<br /> Active Accessibility  的主要思想是提供一种以程序方式访问UI元素信息或操作这些UI元素的功能。支持这种功能的 UI(User Interface)  元素是可访问的。在大多数情况下，这意味着一个UI元素支持 IAccessible 接口。你也可以说在 Active Accessibility  的世界里，一个可访问的UI元素可表示为 IAccessible 接口。<br /> 每当你需要得到有关一个元素的信息，在其上执行一个动作，或者使用 Active Accessibility  做其它的什么，你通常需要通过使用代表这个元素的 IAccessible 接口的一种方法或者属性来引用这个元素。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" /> Active Accessibility  原理<br /> Active Accessibility? 的核心功能由 OLEACC.DLL  提供的。每次当你调用一个函数来返回一个 IAccessible 接口指针，其与一个UI元素相对应，OLEACC.DLL就检查此元素是否内在支持  IAccessible。内在的支持意思是该元素的 IAccessible 是用程序实现的。<br /> 当一个UI元素不能内在的支持 IAccessible 时，OLEACC.DLL 检查该元素的Windows 类名。如果该类是一个 USER 或者  COMCTL32 支持的类，OLEACC.DLL 就创建一个代理为 UI 元素实现 IAccessible 接口。大多数--但不是全部--COMCTL32  控件都具有被 OLEACC.DLL 支持的 IAccessible 接口。<br /> 内在支持  IAccessible 的 UI 元素的例子是定制控件，owner-drawn  和无窗口的控件。因为开发者创建的程序包含这些UI元素，同样就实现了这些元素的接口，他们有责任为这些方法和属性提供正确的支持。<br /> 如果你用标准控件，这也意味着你不必重写你的应用，这些应用自动与Active  Accessibility兼容。<br /> Active Accessibility 名字是基于 Win32 控件的名字给出的，角色基于控件的功能定义。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" /> 如何得到 IAccessible  接口指针<br /> 每当你需要有关一个元素的信息，在其上执行一个动作，或者使用 Active  Accessibility 做其它的什么，你只需要通过使用代表这个元素的 IAccessible 接口的一种方法或者属性来引用这个元素。<br /> 有几种方法取得代表一个可访问 UI 元素的 IAccessible 接口的指针。最普通的方法是使用 Active  Accessibility 提供的一种函数，例如 AccessibleObjectFromPoint，AccessibleObjectFromWindow  等等，或者使用 IAccessible 支持的方法，例如 get_accChild，get_accParent。<br /> IAccessible 接口支持允许你得到各 UI 元素信息的属性，而其中对于例子程序最重要的属性是名字、角色和状态。<br /> Active Accessibility SDK提供了一些方便的工具，其中的 Object Inspector  能显示光标指向的UI元素的属性。Object Inspector 显示了Active Accessibility 的世界如何因为具有支持一个选定窗口内的  IAccessible 接口的控制而变得通用了。除了搜索有关元素的信息和通过 IAccessible 接口控制元素以外，Active  Accessibility? 还有两种对于例子程序非常有用的特性：监视UI元素发生的事件和模拟键盘、鼠标输入。由可访问的元素激发的事件称为  WinEvents，当可访问的元素创建或者名字、状态、位置或者键盘焦点发生变化时，就激发这些事件（事件机制类似于标准的 Windows 的 hook  机制。监视事件我们将在后面介绍。）。这些事件的清单见文件 WINABLE.H。每个事件的名字以 EVENT_OBJECT 或 EVENT_SYSTEM  开始。<br /> 好，我们言归正传，来介绍如何得到 IAccessible 接口指针。前面已经提到过  AccessibleObjectFromWindow 这个 Active Accessibility 提供的函数，从字面上大家可以看出是通过窗口来得到对应的  IAccessible 接口指针。<br /> 因为 IAccessible  接口的数量比窗口要多（因为大多数--但不是全部--COMCTL32 控件都有被 OLEACC.DLL 支持的 IAccessible 接口。），使用 Win32  函数来搜索一个窗口将会比使用 Active Accessibility 树搜索与该窗口相应的 IAccessible  接口要占用少得多的时间。这就意味着为了提高性能，你应该使用 FindWindow 和 EnumWindows 这样的 Win32  函数来找到与希望的UI元素最接近的窗口。当然，在权衡 Win32 函数和 Active Accessibility  函数时，上面的规则只是使用它们的一般标准而不能盲目的遵照执行，重要的是理解它们的本来意义。<br />下面结合代码介绍一下它的用法。<br />我们来得到下面运行窗口的  IAccessible 接口指针。<br /><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/actaccblty1.gif" width="347" height="168" alt="" /><br /><span><br />图一  </span><br /><pre>HWND hWndMainWindow; IAccessible *paccMainWindow = NULL; HRESULT hr; //得到标题为"运行"的窗口的句柄 if(NULL == (hWndMainWindow = FindWindow(NULL, "运行"))) { 	MessageBox(NULL, "没有发现窗口！", "错误", MB_OK); } else { 	//通过窗口句柄得到窗口的 IAccessible 接口指针。 	if(S_OK == (hr = AccessibleObjectFromWindow(hWndMainWindow,  	                                            OBJID_WINDOW,  	                                            IID_IAccessible, 	                                            (void**)&amp;paccMainWindow))) 	{ 		//&#8230;&#8230;我们可以通过这个指针paccMainWindow进行操作。 		paccMainWindow-&gt;Release();         } }    </pre>    现在我们已经得到窗口的 IAccessible  接口指针了（paccMainWindow），那么，我们可以干什么呢？我们怎么得到窗口中某个控件的 IAccessible  接口指针呢？我们就以上面的运行窗口为例。看看如何得到文本框的 IAccessible 接口指针！！<br />    首先我们启动  inspect32.exe，什么？你不知道这是什么东西？赶紧先下载个Active Accessibility SDK看看吧&#8230;&#8230;<br />     然后，把鼠标放到所关注的控件上（即上图中的文本输入框），你会得到如下信息：<br /><br /><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/actaccblty2.gif" width="469" height="541" alt="" /><br />图二<br /><br />我们现在主要关注的信息是：Name、Role、Window  className。 <pre>Name = "打开(O):" Role = "可编辑文字" Window className = "Edit"  </pre>    当开发自定义、owner drawn  或者无窗口的控件时，为同一窗口的每个"角色-名字"指定独一无二的表示是一个非常好的编程习惯。然而，如果由于某种原因，同一窗口中的2个 UI  元素具有同样的"角色-名字"对，那么就需要增加一个参数--windows 类--以唯一的来表示这个元素。<br />    FindChild 函数显示了一个基于  Active Accessibility  父/子（你可以理解成父窗口/子窗口的关系，只是为了便于理解:-P）导航的搜索例程的实现。这个函数有6个参数。前4个包含传递给函数的信息，后2个包含了  IAccessible 接口/子ID对（见附录）。<br />下面我们开始取文本输入框的 IAccessible 接口指针。 <pre>IAccessible*	paccControl = NULL;//输入框的 IAccessible 接口 VARIANT		varControl;    		//子ID。  FindChild( paccMainWindow,             "打开(O):",             "可编辑文字",             "Edit",             &amp;paccControl,             &amp;varControl )    </pre>第一个参数是先前得到的窗口 IAccessible  接口指针。<br />第二、三、四个参数分别是名字、角色、类。<br />后2个为返回参数包含了 IAccessible  接口/子ID对。下面是FindChild的实现。 <pre>BOOL FindChild (IAccessible* paccParent,                           LPSTR szName, LPSTR szRole,                           LPSTR szClass,                           IAccessible** paccChild,                           VARIANT* pvarChild) { 	HRESULT hr; 	long numChildren; 	unsigned long numFetched; 	VARIANT varChild; 	int index; 	IAccessible* pCAcc = NULL; 	IEnumVARIANT* pEnum = NULL; 	IDispatch* pDisp = NULL; 	BOOL found = false; 	char szObjName[256], szObjRole[256], szObjClass[256], szObjState[256]; 	 		//得到父亲支持的IEnumVARIANT接口 	hr = paccParent -&gt; QueryInterface(IID_IEnumVARIANT, (PVOID*) &amp; pEnum); 	 	if(pEnum) 		pEnum -&gt; Reset(); 	 //取得父亲拥有的可访问的子的数目 	paccParent -&gt; get_accChildCount(&amp;numChildren); 	 //搜索并比较每一个子ID，找到名字、角色、类与输入相一致的。 	for(index = 1; index &lt;= numChildren &amp;&amp; !found; index++) 	{ 		pCAcc = NULL;		 		// 如果支持IEnumVARIANT接口，得到下一个子ID //以及其对应的 IDispatch 接口 		if (pEnum) 			hr = pEnum -&gt; Next(1, &amp;varChild, &amp;numFetched);	 		else 	{ 		//如果一个父亲不支持IEnumVARIANT接口，子ID就是它的序号 			varChild.vt = VT_I4; 			varChild.lVal = index; 		} 		 		// 找到此子ID对应的 IDispatch 接口 		if (varChild.vt == VT_I4) 		{ 			//通过子ID序号得到对应的 IDispatch 接口 			pDisp = NULL; 			hr = paccParent -&gt; get_accChild(varChild, &amp;pDisp); 		} 		else 			//如果父支持IEnumVARIANT接口可以直接得到子IDispatch 接口 			pDisp = varChild.pdispVal; 		 		// 通过 IDispatch 接口得到子的 IAccessible 接口 pCAcc 		if (pDisp) 		{ 			hr = pDisp-&gt;QueryInterface(IID_IAccessible, (void**)&amp;pCAcc); 			hr = pDisp-&gt;Release(); 		} 		 		// Get information about the child 		if(pCAcc) 		{ 			//如果子支持IAccessible 接口，那么子ID就是CHILDID_SELF 			VariantInit(&amp;varChild); 			varChild.vt = VT_I4; 			varChild.lVal = CHILDID_SELF; 			 			*paccChild = pCAcc; 		} 		else 			//如果子不支持IAccessible 接口 			*paccChild = paccParent; 		 		//跳过了有不可访问状态的元素 		GetObjectState(*paccChild,  		               &amp;varChild,  		               szObjState,  		               sizeof(szObjState)); 		if(NULL != strstr(szObjState, "unavailable")) 		{ 			if(pCAcc) 				pCAcc-&gt;Release(); 			continue; 		} 		//通过get_accName得到Name 		GetObjectName(*paccChild, &amp;varChild, szObjName, sizeof(szObjName)); 		//通过get_accRole得到Role 		GetObjectRole(*paccChild, &amp;varChild, szObjRole, sizeof(szObjRole)); 		//通过WindowFromAccessibleObject和GetClassName得到Class 		GetObjectClass(*paccChild, szObjClass, sizeof(szObjClass)); 		//以上实现代码比较简单，大家自己看代码吧。  			//如果这些参数与输入相符或输入为NULL 		if ((!szName ||  		     !strcmp(szName, szObjName)) &amp;&amp;  		     (!szRole ||  		      !strcmp(szRole, szObjRole)) &amp;&amp;  		     (!szClass ||  		      !strcmp(szClass, szObjClass))) 		{ 			found = true; 			*pvarChild = varChild; 			break; 		} 		if(!found &amp;&amp; pCAcc) 		{ 			// 以这次得到的子接口为父递归调用 			found = FindChild(pCAcc,  			                  szName,  			                  szRole,  			                  szClass,  			                  paccChild,  			                  pvarChild); 			if(*paccChild != pCAcc) 				pCAcc-&gt;Release(); 		} 	}//End for 	 	// Clean up 	if(pEnum) 		pEnum -&gt; Release(); 	 	return found; }  // UI元素的状态也表示成整型形式。因为一个状态可以有多个值， //例如可选的、可做焦点的，该整数是反映这些值的位的或操作结果。 //将这些或数转换成相应的用逗号分割的状态字符串。 UINT GetObjectState(IAccessible* pacc,                      VARIANT* pvarChild,                      LPTSTR lpszState,                      UINT cchState) {     HRESULT hr;     VARIANT varRetVal; 	     *lpszState = 0; 	     VariantInit(&amp;varRetVal); 	     hr = pacc-&gt;get_accState(*pvarChild, &amp;varRetVal); 	 	if (!SUCCEEDED(hr))         return(0); 	 	DWORD dwStateBit; 	int cChars = 0;     if (varRetVal.vt == VT_I4) 	{ 		// 根据返回的状态值生成以逗号连接的字符串。         for (dwStateBit = STATE_SYSTEM_UNAVAILABLE;                 dwStateBit &lt; STATE_SYSTEM_ALERT_HIGH;                 dwStateBit &lt;&lt;= 1)         {             if (varRetVal.lVal &amp; dwStateBit)             {                 cChars += GetStateText(dwStateBit,                                         lpszState + cChars,                                         cchState - cChars); 				*(lpszState + cChars++) = '','';             }         } 		if(cChars &gt; 1) 			*(lpszState + cChars - 1) = ''\0'';     }     else if (varRetVal.vt == VT_BSTR)     {         WideCharToMultiByte(CP_ACP,                              0,                              varRetVal.bstrVal,                              -1,                              lpszState,                             cchState,                              NULL,                              NULL);     } 	     VariantClear(&amp;varRetVal); 	     return(lstrlen(lpszState)); }</pre>好了！！我们已经成功得到文本框的 IAccessible 接口指针了！！现在你可以用这个接口指针为所欲为了！！！呵呵：)<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" /> 在 IAccessible 接口上执行动作<br />    有了表示一个可访问的 UI 元素的  IAccessible  接口/子ID对，你也有了搜索该元素一个名字（get_accName）、角色（get_accRole）、类和状态（get_accState）的方法。让我们看看你还可以干什么！get_accDescription  能取得UI元素的描述，get_accValue 能取得一个值。<br />    最重要的函数之一是  accDoDefaultAction。每个可访问的UI元素都有一个缺省定义的动作。例如，一个按钮的缺省动作是"按下这个按钮"，一个检查框的缺省动作是"不选"。为了确定一个元素的缺省动作，请参考  Active Accessibility 文档或者调用 get_accDefaultAction。<br />     如果我想起动注册表编辑器，该怎么办呢？如果是我们手动做的话，无非是在文本输入框输入"regedit"，然后按确定按钮，就这么简单。下面我们来看看用 Active  Accessibility 是怎么来实现的。 <pre>//在文本输入框输入"regedit" if(1 == FindChild (paccMainWindow, "打开(O):",                     "可编辑文字",                     "Edit",                     &amp;paccControl,                     &amp;varControl)) { 	//在这里修改文本编辑框的值 	hr = paccControl-&gt;put_accValue(varControl,  	                                  CComBSTR("regedit")); 	paccControl-&gt;Release(); 	VariantClear(&amp;varControl); } 		 // 找到确定按钮，并执行默认动作。 if(1 == FindChild (paccMainWindow,                     "确定",                     "按下按钮",                     "Button",                     &amp;paccControl,                     &amp;varControl)) { 	//这里执行按钮的默认动作，即"按下这个按钮" 	hr = paccControl-&gt;accDoDefaultAction(varControl); 	paccControl-&gt;Release(); 	VariantClear(&amp;varControl); }</pre>现在，你会发现已经成功启动了注册表编辑器！！<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" />  模拟键盘和鼠标输入<br />    让我们假设你需要操作一个新的不完全支持 Windows 消息和 IAccessible 接口方法的 UI  元素。如果它不支持你需要的消息和方法，最简单的解决办法就是模拟键盘和鼠标输入。例如，你可以用Tab模拟转移到期望的控件。<br />     使你能够实现这些的函数就是 SendInput 一个一般的USER API。虽然不属于Active  Accessibility，把他们联合使用很自然。<br />    SendInput  接受三个参数：要执行的鼠标键盘动作个数、INPUT结构数组和结构数组的大小。每个INPUT结构描述一个要执行的动作。注意，按下一个按钮和释放一个按钮是两个不同的动作，所以必须创建两个不同的INPUT结构。<br />下面的代码将模拟  ALT+F4 按键来关闭窗口。 <pre>INPUT input[4];	 memset(input, 0, sizeof(input));  //设置模拟键盘输入 input[0].type = input[1].type = input[2].type = input[3].type = INPUT_KEYBOARD; input[0].ki.wVk  = input[2].ki.wVk = VK_MENU; input[1].ki.wVk  = input[3].ki.wVk = VK_F4;  // 释放按键，这非常重要 input[2].ki.dwFlags = input[3].ki.dwFlags = KEYEVENTF_KEYUP;  SendInput(4, input, sizeof(INPUT));</pre>具体用法大家还是查MSDN吧，这里就不罗嗦了！！：）<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" /> 监视WinEvents<br />    监视 WinEvents 非常像通过 Windows Hook  监视 Windows 消息。最重要的区别就是从另一个进程监视 UI 元素发出的 WinEvents  时，你不需要创建一个单独的DLL来注入那个进程的地址空间。<br />    监视 WinEvents 有两种选择：通过设置 SetWinEventHook  函数的最后一个参数来确定是在上下文之外还是之内监视。如果是在上下文之外，不需要额外的DLL，回调函数运行在目标进程之外。如果是在上下文之内，回调函数必须放在额外的DLL，并注入目标进程的地址空间。第二种方法写代码比较麻烦，但是运行效率高。<br />     好，现在回到上面的例子。上面例子能够执行的前提条件是能够找到标题为"运行"的窗口。现在可以先检查运行窗口是否存在，如果不存在就设置WinEvents  钩子去监视，直到"运行"窗口被创建。看下面代码： <pre>if(NULL == (hWndMainWindow = FindWindow(NULL, szMainTitle))) { hEventHook = SetWinEventHook( 	EVENT_MIN,	// eventMin ID 	EVENT_MAX,	// eventMax ID 	NULL,		// always NULL for outprocess hook 	WinCreateNotifyProc,		// call back function 	0,				// idProcess 	0,				// idThread           // always the same for outproc hook 	WINEVENT_SKIPOWNPROCESS | WINEVENT_OUTOFCONTEXT); }    </pre>第一、二个参数用来指定监视事件的范围。第四个参数是定义的回调函数。<br />下面是回调函数： <pre>void CALLBACK WinCreateNotifyProc(  HWINEVENTHOOK  hEvent,  DWORD   event,  HWND    hwndMsg,  LONG    idObject,  LONG    idChild,  DWORD   idThread,  DWORD   dwmsEventTime  ) { 	 	if( event != EVENT_OBJECT_CREATE) 		return; 	 	char bufferName[256]; 	IAccessible *pacc=NULL; 	VARIANT varChild;     VariantInit(&amp;varChild); 	//得到触发事件的 UI 元素的 IAccessible 接口/子ID对 	HRESULT hr= AccessibleObjectFromEvent(hwndMsg,  	                                      idObject,  	                                      idChild,  	                                      &amp;pacc,  	                                      &amp;varChild); 	 	if(!SUCCEEDED(hr)) 	{ 		VariantClear(&amp;varChild); 		return; 	} 	//得到 UI 元素的Name，并比较，如果是"运行"就发送消息给主线程。 	GetObjectName(pacc, &amp;varChild, bufferName, sizeof(bufferName)); 	if(strstr(bufferName, szMainTitle)) 		PostThreadMessage(GetCurrentThreadId(),  		                  WM_TARGET_WINDOW_FOUND,  		                  0,  		                  0); 	 	return; }    </pre>恩&#8230;&#8230;&#8230;&#8230;，一个应用基本成型了，虽然比较简单。就先写这么多吧，请关注后续介绍。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" /> 附录：<br /><br />关于IAccessible 接口/子ID对：<br />     让我们来考虑这样一个控件，他支持 IAccessible 接口并且包含一些子控件，比如 listbox 就包含很多 items  。有两种方法让他可以被访问：第一种，提供listbox的 IAccessible 接口和每一个 item 自己的 IAccessible  接口。另一种是只提供一个控件的 IAccessible 接口，这个接口能够提供基于某种识别方法来访问每一个子控件的功能。<br />     第一种方法，需要为这个控件和每一个子控件创建单独的 COM 对象，这会比第二种方法（每一个子控件不支持自己的 IAccessible  接口，而是通过父接口来访问）增加内存消耗。第二种方法里，通过增加一个参数--子ID--同父的IAccessible 接口一起表示这个子控件。子ID 是一个  VT_I4 型的 VARIANT  值，包含一个由程序决定的独特的值，或只是一个子控件的序号。序号意味着第一个子控件的ID为1，第二个子控件的ID为2，依次增长！<br />     这样，如果一个子控件不支持自己的 IAccessible 接口，而其父控件支持，那么这个子控件可以用它的父控件的 IAccessible 接口/子ID  对来表示。通常，一个支持 IAccessible 接口的父UI元素也是通过这样的 IAccessible 接口/子对表示的，这时候其子ID号为  CHILDID_SELF （就是0）。<br />    记住，子ID号总是相对于 IAccessible 接口的。例如，一个可访问的元素可以同相对于其父  IAccessible 接口的一个非子 CHILDID_SELF 的 ID 及其父IAccessible 接口表示，如果他支持 IAccessible  接口，此元素的子ID就是相对于自己 IAccessible 接口的CHILDID_SELF。<br />    呵呵，翻译的有点别扭，意思就是说，如果这个控件支持  IAccessible 接口，那么它的子ID就是0（CHILDID_SELF），可以用它自己的 IAccessible  接口和0这个对来表示这个控件。如果控件不支持 IAccessible 接口，就用它父控件的 IAccessible 接口，和一个相对于父 IAccessible  接口的子ID来表示。哎呀！！不知道说明白没有。郁闷！！！！<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" />  注：<br />     我也是刚开始学习怎么使用MSAA，但是苦于很难找到中文资料。希望这篇文章对大家能有所帮助。由于了解的还很肤浅，错误难免，望谅解！！：）<br />     还有，这篇文章基本编译自Dmitri Klementiev的《Software Driving Software: Active  Accessibility-Compliant Apps Give Programmers New Tools to Manipulate  Software》，只是按自己的理解重新编排了一下，如果觉得不符合自己的学习习惯可以看原文。并且我的文章省略了很多东西，呵呵。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif"  alt="" /> 参考资料：  <ul> <li>1、 Dmitri Klementiev写的《Software Driving Software: Active  Accessibility-Compliant Apps Give Programmers New Tools to Manipulate  Software》及其源程序。http://msdn.microsoft.com/msdnmag/issues/0400/aaccess/default.aspx   </li><li>2、 MSDN中的相关章节。&nbsp;<br /><br /><br />转自：http://www.vckbase.com/document/viewdoc/?id=883</li></ul></div><img src ="http://www.cppblog.com/flyinghare/aggbug/156477.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2011-09-21 22:33 <a href="http://www.cppblog.com/flyinghare/archive/2011/09/21/156477.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DLL入门浅析（5）——使用DLL在进程间共享数据</title><link>http://www.cppblog.com/flyinghare/archive/2011/09/19/156262.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Mon, 19 Sep 2011 13:04:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2011/09/19/156262.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/156262.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2011/09/19/156262.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/156262.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/156262.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #ffffff; "><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;在Win16环境中，DLL的全局数据对每个载入它的进程来说都是相同的,因为所有的进程用的都收同一块地址空间；而在Win32环境中，情况却发生了变化，每个进程都有了它自己的地址空间，DLL函数中的代码所创建的任何对象（包括变量）都归调用它的进程所有。当进程在载入DLL时，操作系统自动把DLL地址映射到该进程的私有空间，也就是进程的虚拟地址空间，而且也复制该DLL的全局数据的一份拷贝到该进程空间。（在物理内存中，多进程载入DLL时，DLL的代码段实际上是只加载了一次，只是将物理地址映射到了各个调用它的进程的虚拟地址空间中，而全局数据会在每个进程都分别加载）。也就是说每个进程所拥有的相同的DLL的全局数据，它们的名称相同，但其值却并不一定是相同的，而且是互不干涉的。<br />因此，在Win32环境下要想在多个进程中共享数据，就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来，放置在一个独立的数据段里，并把该段的属性设置为共享。必须给这些变量赋初值，否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">在DLL的实现文件中添加下列代码：</p><div style="border-right-color: #cccccc; border-right-width: 1px; border-right-style: solid; padding-right: 5px; border-top-color: #cccccc; border-top-width: 1px; border-top-style: solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left-color: #cccccc; border-left-width: 1px; border-left-style: solid; width: 760px; word-break: break-all; padding-top: 4px; border-bottom-color: #cccccc; border-bottom-width: 1px; border-bottom-style: solid; background-color: #eeeeee; "><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #000000; ">#pragma&nbsp;data_seg(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">DLLSharedSection</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;声明共享数据段，并命名该数据段</span><span style="color: #008000; "><br /><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; ">int</span><span style="color: #000000; ">&nbsp;SharedData&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">123</span><span style="color: #000000; ">;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;必须在定义的同时进行初始化!!!!</span><span style="color: #008000; "><br /><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #000000; ">#pragma&nbsp;data_seg()</span></div><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&nbsp;</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&nbsp;在#pragma data_seg("DLLSharedSection")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。仅定义一个数据段还不能达到共享数据的目的，还要告诉编译器该段的属性，有三种方法可以实现该目的（其效果是相同的），一种方法是在.DEF文件中加入如下语句：</p><div style="border-right-color: #cccccc; border-right-width: 1px; border-right-style: solid; padding-right: 5px; border-top-color: #cccccc; border-top-width: 1px; border-top-style: solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left-color: #cccccc; border-left-width: 1px; border-left-style: solid; width: 760px; word-break: break-all; padding-top: 4px; border-bottom-color: #cccccc; border-bottom-width: 1px; border-bottom-style: solid; background-color: #eeeeee; "><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #000000; ">SETCTIONS<br /><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;DLLSharedSection&nbsp;READ&nbsp;WRITE&nbsp;SHARED</span></div><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&nbsp;</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">另一种方法是在项目设置的链接选项(Project Setting --〉Link)中加入如下语句：</p><div style="border-right-color: #cccccc; border-right-width: 1px; border-right-style: solid; padding-right: 5px; border-top-color: #cccccc; border-top-width: 1px; border-top-style: solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left-color: #cccccc; border-left-width: 1px; border-left-style: solid; width: 760px; word-break: break-all; padding-top: 4px; border-bottom-color: #cccccc; border-bottom-width: 1px; border-bottom-style: solid; background-color: #eeeeee; "><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #000000; ">/</span><span style="color: #000000; ">SECTION:DLLSharedSection,rws</span></div><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&nbsp;</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">还有一种就是使用指令：</p><div style="border-right-color: #cccccc; border-right-width: 1px; border-right-style: solid; padding-right: 5px; border-top-color: #cccccc; border-top-width: 1px; border-top-style: solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left-color: #cccccc; border-left-width: 1px; border-left-style: solid; width: 760px; word-break: break-all; padding-top: 4px; border-bottom-color: #cccccc; border-bottom-width: 1px; border-bottom-style: solid; background-color: #eeeeee; "><span style="color: #000000; ">#pragma&nbsp;comment(linker,</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/section:.DLLSharedSection,rws</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)</span></div><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br />那么这个数据节中的数据可以在所有DLL的实例之间共享了。所有对这些数据的操作都针对同一个实例的，而不是在每个进程的地址空间中都有一份。<br />&nbsp;<br />当进程隐式或显式调用一个动态库里的函数时，系统都要把这个动态库映射到这个进程的虚拟地址空间里。这使得DLL成为进程的一部分，以这个进程的身份执行，使用这个进程的堆栈。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">下面来谈一下在具体使用共享数据段时需要注意的一些问题：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所有在共享数据段中的变量，只有在数据段中经过了初始化之后，才会是进程间共享的。如果没有初始化，那么进程间访问该变量则是未定义的。<br />&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所有的共享变量都要放置在共享数据段中。如何定义很大的数组，那么也会导致很大的DLL。<br />&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不要在共享数据段中存放进程相关的信息。Win32中大多数的数据结构和值（比如HANDLE）只在特定的进程上下文中才是有效地。<br />&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个进程都有它自己的地址空间。因此不要在共享数据段中共享指针，指针指向的地址在不同的地址空间中是不一样的。<br />&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DLL在每个进程中是被映射在不同的虚拟地址空间中的，因此函数指针也是不安全的。<br /><br />当然还有其它的方法来进行进程间的数据共享，比如文件内存映射等，这就涉及到通用的进程间通信了，这里就不多讲了。</p></span><br /><br />转自：<a href="http://www.cppblog.com/suiaiguo/archive/2009/07/21/90734.html">http://www.cppblog.com/suiaiguo/archive/2009/07/21/90734.html</a><img src ="http://www.cppblog.com/flyinghare/aggbug/156262.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2011-09-19 21:04 <a href="http://www.cppblog.com/flyinghare/archive/2011/09/19/156262.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows系统编程之异步I/O和完成端口</title><link>http://www.cppblog.com/flyinghare/archive/2011/06/29/149714.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 29 Jun 2011 02:13:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2011/06/29/149714.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/149714.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2011/06/29/149714.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/149714.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/149714.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="font-family: Arial, sans-serif, Helvetica, Tahoma; line-height: 25px; "><p class="pediy" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; "><span style="color: blue; "><span class="pediy" style="font-size: 9pt; ">标 题:</span></span><span class="pediy" style="font-size: 9pt; "><span class="pediy" style="font-size: 9pt; "><span class="pediy" style="font-size: 9pt; ">&nbsp;Windows系统编程之异步I/O和完成端口<br /><span style="color: blue; ">作 者:&nbsp;</span>北极星2003<br /><span style="color: blue; ">时 间:&nbsp;</span><span style="color: #666686; ">2006-07-02 18:46&nbsp;</span><br /><span style="color: blue; ">链 接:&nbsp;</span><span style="color: #999999; "><a href="http://bbs.pediy.com/showthread.php?threadid=28342" style="color: #000099; text-decoration: none; "><span style="color: #666686; ">http://bbs.pediy.com/showthread.php?threadid=28342</span></a></span>&nbsp;<br /><span style="color: blue; ">详细信息:</span></span></span></span></p><p class="pediy" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; ">&nbsp;</p><p class="pediy" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; "><span style="font-size: medium; ">Windows系统编程之异步I/O和完成端口</span><br />【作者】北极星2003<br />【来源】看雪技术论坛（bbs.pediy.com）&nbsp;<br />【时间】2006年7月1日<br /><br />一、&nbsp;&nbsp;同步I/O和异步I/O<br /><br />在介绍这部分内容之前先来认识下&#8220;异步I/O&#8221;。<br />&nbsp;&nbsp;说起异步IO，很容易联想到同步I/O，对于同一个I/O对象句柄在同一时刻只允许一个I/O操作，其原理如下图所示：<br />&nbsp;<br /><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/201106301.gif" width="536" height="236" alt="" /><br />&nbsp;&nbsp;显然，当内核真正处理I/O的时间段（T2~T4），用户线程是处于等待状态的，如果这个时间段比较段的话，没有什么影响；倘若这个时间段很长的话，线程就会长时间处于挂起状态。事实上，该线程完全可以利用这段时间用处理其他事务。<br /><br />&nbsp;&nbsp;异步I/O恰好可以解决同步I/O中的问题，而且支持对同一个I/O对象的并行处理，其原理如下图所示：<br />&nbsp;<img src="http://www.cppblog.com/images/cppblog_com/flyinghare/201106302.gif" width="549" height="245" alt="" /><br /><br />&nbsp;&nbsp;异步I/O在I/O请求完成时，可以使用让I/O对象或者事件对象受信来通知用户线程，而用户线程中可以使用GetOverlappedResult来查看I/O的执行情况。<br />&nbsp;&nbsp;<br />由于异步I/O在进行I/O请求后会立即返回，这样就会产生一个问题：&#8220;程序是如何取得I/O处理的结果的？&#8221;。<br /><br />&nbsp;&nbsp;有多种方法可以实现异步I/O，其不同资料上的分类一般都不尽相同，但原理上都类似，这里我把实现异步I/O的方法分为3类，本文就针对这3类方法进行详细的讨论。<br />（1）重叠I/O<br />（2）异步过程调用（APC），扩展I/O<br />（3）使用完成端口（IOCP）<br /><br />二、使用重叠I/O实现异步I/O<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;同一个线程可以对多个I/O对象进行I/O操作，不同的线程也可以对同一个I/O对象进行操作，在我的理解中，重叠的命名就是这么来的。<br /><br />&nbsp;&nbsp;在使用重叠I/O时，线程需要创建OVERLAPPED结构以供I/O处理。该结构中最重要的成员是hEvent，它是作为一个同步对象而存在，如果hEvent为NULL，那么此时的同步对象即为文件句柄、管道句柄等I/O操作对象。当I/O完成后，会使这里的同步对象受信，从而通知用户线程。<br /><br />&nbsp;&nbsp;由于在进行I/O请求后会立即返回，但有时用户线程需要知道I/O当前的执行情况，此时就可以使用GetOverlappedResult。如果该函数的bWait参数为true,那么改函数就会阻塞线程直到目标I/O处理完成为止；如果bWait为false，那么就会立即返回，如果此时的I/O尚未完，调用GetLastError就会返回ERROR_IO_INCOMPLETE。<br /><br />代码示例一：</p><blockquote style="border-left-width: 1px; border-left-style: solid; border-left-color: #cccccc; margin-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; background-color: #fafafa; "><span class="p9" style="font-size: 9pt; text-decoration: none; "><pre style="font-family: monospace; font-size: 1em; "><span class="p9" style="font-size: 9pt; text-decoration: none; ">代码:<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " />
DWORD&nbsp;&nbsp;&nbsp;nReadByte&nbsp;;
BYTE&nbsp;&nbsp;&nbsp;bBuf[BUF_SIZE]&nbsp;;
OVERLAPPED&nbsp;ov&nbsp;=&nbsp;{&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;NULL&nbsp;}&nbsp;;&nbsp;&nbsp;//&nbsp;hEvent&nbsp;=&nbsp;NULL&nbsp;;
HANDLE&nbsp;hFile&nbsp;=&nbsp;CreateFile&nbsp;(&nbsp;&#8230;&#8230;,&nbsp;FILE_FLAG_OVERLAPPED,&nbsp;&#8230;&#8230;&nbsp;)&nbsp;;
ReadFile&nbsp;(&nbsp;hFile,&nbsp;bBuf,&nbsp;sizeof(bBuf),&nbsp;&amp;nReadByte,&nbsp;&amp;ov&nbsp;)&nbsp;;
//&nbsp;由于此时hEvent=NULL，所以同步对象为hFile,下面两句的效果一样
WaitForSingleObject&nbsp;(&nbsp;hFile,&nbsp;INFINITE&nbsp;)&nbsp;;
//GetOverlappedResult&nbsp;(&nbsp;hFile,&nbsp;&amp;ov,&nbsp;&amp;nRead,&nbsp;TRUE&nbsp;)&nbsp;;
<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " /></span></pre><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; ">&nbsp;</p></span></blockquote><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; "><br />这段代码在调用ReadFile后会立即返回，但在随后的WaitForSingleObject或者GetOverlappedResult中阻塞，利用同步对象hFile进行同步。<br /><br />&nbsp;&nbsp;这段代码在这里可以实现正常的异步I/O，但存在一个问题，倘若现在需要对hFile句柄进行多个I/O操作，就会出现问题。见下面这段代码。<br /><br />代码示例二：</p><blockquote style="border-left-width: 1px; border-left-style: solid; border-left-color: #cccccc; margin-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; background-color: #fafafa; "><span class="p9" style="font-size: 9pt; text-decoration: none; "><pre style="font-family: monospace; font-size: 1em; "><span class="p9" style="font-size: 9pt; text-decoration: none; ">代码:<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " />
DWORD&nbsp;&nbsp;&nbsp;nReadByte&nbsp;;
BYTE&nbsp;&nbsp;&nbsp;bBuf1[BUF_SIZE],bBuf2[BUF_SIZE],bBuf3[BUF_SIZE]&nbsp;;
OVERLAPPED&nbsp;ov1&nbsp;=&nbsp;{&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;NULL&nbsp;}&nbsp;;&nbsp;&nbsp;
OVERLAPPED&nbsp;ov2&nbsp;=&nbsp;{&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;NULL&nbsp;}&nbsp;;&nbsp;&nbsp;
OVERLAPPED&nbsp;ov3&nbsp;=&nbsp;{&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;NULL&nbsp;}&nbsp;;&nbsp;&nbsp;
HANDLE&nbsp;hFile&nbsp;=&nbsp;CreateFile&nbsp;(&nbsp;&#8230;&#8230;,&nbsp;FILE_FLAG_OVERLAPPED,&nbsp;&#8230;&#8230;&nbsp;)&nbsp;;
ReadFile&nbsp;(&nbsp;hFile,&nbsp;bBuf1,&nbsp;sizeof(bBuf1),&nbsp;&amp;nReadByte,&nbsp;&amp;ov1&nbsp;)&nbsp;;
ReadFile&nbsp;(&nbsp;hFile,&nbsp;bBuf2,&nbsp;sizeof(bBuf2),&nbsp;&amp;nReadByte,&nbsp;&amp;ov2&nbsp;)&nbsp;;
ReadFile&nbsp;(&nbsp;hFile,&nbsp;bBuf3,&nbsp;sizeof(bBuf3),&nbsp;&amp;nReadByte,&nbsp;&amp;ov3&nbsp;)&nbsp;;
//假设三个I/O处理的时间比较长，到这里还没有结束
GetOverlappedResult&nbsp;(&nbsp;hFile,&nbsp;&amp;ov1,&nbsp;&amp;nRead,&nbsp;TRUE&nbsp;)&nbsp;;
<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " /></span></pre><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; ">&nbsp;</p></span></blockquote><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; "><br />&nbsp;&nbsp;这里对于hFile有三个重叠的I/O操作，但他们的同步对象却都为hFile。使用GetOverlappedResult进行等待操作，这里看似在等待第一个I/O处理的完成，其实只要有任何一个I/O处理完成，该函数就会返回，相当于忽略了其他两个I/O操作的结果。<br /><br />&nbsp;&nbsp;其实，这里有一个很重要的原则：对于一个重叠句柄上有多于一个I/O操作的时候，应该使用事件对象而不是文件句柄来实现同步。正确的实现见示例三。<br />&nbsp;&nbsp;<br />代码示例三：</p><blockquote style="border-left-width: 1px; border-left-style: solid; border-left-color: #cccccc; margin-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; background-color: #fafafa; "><span class="p9" style="font-size: 9pt; text-decoration: none; "><pre style="font-family: monospace; font-size: 1em; "><span class="p9" style="font-size: 9pt; text-decoration: none; ">代码:<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " />
DWORD&nbsp;&nbsp;&nbsp;nReadByte&nbsp;;
BYTE&nbsp;&nbsp;&nbsp;bBuf1[BUF_SIZE],bBuf2[BUF_SIZE],bBuf3[BUF_SIZE]&nbsp;;
HANDLE&nbsp;&nbsp;hEvent1&nbsp;=&nbsp;CreateEvent&nbsp;(&nbsp;NULL,&nbsp;FALSE,&nbsp;FALSE,&nbsp;NULL&nbsp;)&nbsp;;&nbsp;
HANDLE&nbsp;&nbsp;hEvent2&nbsp;=&nbsp;CreateEvent&nbsp;(&nbsp;NULL,&nbsp;FALSE,&nbsp;FALSE,&nbsp;NULL&nbsp;)&nbsp;;
HANDLE&nbsp;&nbsp;hEvent3&nbsp;=&nbsp;CreateEvent&nbsp;(&nbsp;NULL,&nbsp;FALSE,&nbsp;FALSE,&nbsp;NULL&nbsp;)&nbsp;;
OVERLAPPED&nbsp;ov1&nbsp;=&nbsp;{&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;hEvent1&nbsp;}&nbsp;;&nbsp;&nbsp;
OVERLAPPED&nbsp;ov2&nbsp;=&nbsp;{&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;hEvent2&nbsp;}&nbsp;;&nbsp;&nbsp;
OVERLAPPED&nbsp;ov3&nbsp;=&nbsp;{&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;hEvent3&nbsp;}&nbsp;;&nbsp;&nbsp;
HANDLE&nbsp;hFile&nbsp;=&nbsp;CreateFile&nbsp;(&nbsp;&#8230;&#8230;,&nbsp;FILE_FLAG_OVERLAPPED,&nbsp;&#8230;&#8230;&nbsp;)&nbsp;;
ReadFile&nbsp;(&nbsp;hFile,&nbsp;bBuf1,&nbsp;sizeof(bBuf1),&nbsp;&amp;nReadByte,&nbsp;&amp;ov1&nbsp;)&nbsp;;
ReadFile&nbsp;(&nbsp;hFile,&nbsp;bBuf2,&nbsp;sizeof(bBuf2),&nbsp;&amp;nReadByte,&nbsp;&amp;ov2&nbsp;)&nbsp;;
ReadFile&nbsp;(&nbsp;hFile,&nbsp;bBuf3,&nbsp;sizeof(bBuf3),&nbsp;&amp;nReadByte,&nbsp;&amp;ov3&nbsp;)&nbsp;;
//此时3个I/O操作的同步对象分别为hEvent1,hEvent2,hEvent3
GetOverlappedResult&nbsp;(&nbsp;hFile,&nbsp;&amp;ov1,&nbsp;&amp;nRead,&nbsp;TRUE&nbsp;)&nbsp;;
<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " /></span></pre><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; ">&nbsp;</p></span></blockquote><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; "><br />&nbsp;&nbsp;这样，这个GetOverlappedResult就可以实现对第一个I/O处理的等待<br />关于重叠I/O的就讨论到这里，关于重叠I/O的实际应用，可以参考《Windows系统编程之进程通信》其中的命名管道实例。<br />http://bbs.pediy.com/showthread.php?s=&amp;threadid=26252<br />&nbsp;<br />三、&nbsp;&nbsp;使用异步过程调用实现异步I/O<br /><br />异步过程调用（APC），即在特定的上下文中异步的执行一个调用。在异步I/O中可以使用APC，即让操作系统的IO系统在完成异步I/O后立即调用你的程序。（在有些资料中，把异步I/O中的APC称为&#8220;完成例程&#8221;，感觉这个名称比较贴切，下文就以&#8220;完成例程&#8221;来表述。另外通常APC是作为线程同步这一块的内容，这里尽量淡化这个概念以免混淆。关于APC的详细内容到线程同步时再介绍&nbsp;）<br /><br />这里需要注意三点：<br />（1）&nbsp;&nbsp;APC总是在调用线程中被调用；<br />（2）&nbsp;&nbsp;当执行APC时，调用线程会进入可变等待状态；<br />（3）&nbsp;&nbsp;线程需要使用扩展I/O系列函数，例如ReadFileEx,WriteFileEx,&nbsp;另外可变等待函数也是必须的（至少下面其中之一）：<br />WaitForSingleObjectEx<br />WaitForMultipleObjectEx<br />SleepEx<br />SignalObjectAndWait<br />MsgWaitForMultipleObjectsEx<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;在使用ReadFileEx,WriteFileEx时，重叠结构OVERLAPPED中的hEvent成员并非一定要指定，因为系统会忽略它。当多个IO操作共用同一个完成例程时，可以使用hEvent来携带序号等信息，用于区别不同的I/O操作，因为该重叠结构会传递给完成例程。如果多个IO操作使用的完成例程都不相同时，则直接把hEvent设置为NULL就可以了。<br /><br />在系统调用完成例程有两个条件：<br />（1）&nbsp;&nbsp;I/O操作必须完成<br />（2）&nbsp;&nbsp;调用线程处于可变等待状态<br /><br />对于第一个条件比较容易，显然完成例程只有在I/O操作完成时才调用；至于第二个条件就需要进行认为的控制，通过使用可变等待函数，让调用线程处于可变等待状态，这样就可以执行完成例程了。这里可以通过调节调用可变等待函数的时机来控制完成例程的执行，即可以确保完成例程不会被过早的执行。<br /><br />当线程具有多个完成例程时，就会形成一个队列。使用可变等待函数使线程进入可变等待状态时有一个表示超时值的参数，如果使用INFINITE，那么只有所有排队的完成例程被执行或者句柄获得信号时该等待函数才返回。<br /><br />上面已经对利用完成例程实现异步I/O的一些比较重要的细节进行的简洁的阐述，接下来就以一个实例来说明完成例程的具体实现过程。<br /><br /><br /><br />实例一：使用完成例程的异步I/O示例<br /><br />1、&nbsp;&nbsp;设计目标<br />体会完成例程的异步I/O实现原理及过程。<br /><br />2、&nbsp;&nbsp;问题的分析与设计<br />设计流程图如下：<br />&nbsp;<img src="http://www.cppblog.com/images/cppblog_com/flyinghare/201106303.gif" alt="" /><br />示图说明：<br />&nbsp;&nbsp;三个IO操作分别是IO_A,&nbsp;IO_B,&nbsp;IO_C,&nbsp;他们的完成例程分别是APC_A,&nbsp;APC_B,&nbsp;APC_C。IO_A,&nbsp;IO_B是两个很短的IO操作，IO_C是一个比较费时的IO操作。<br />3、&nbsp;&nbsp;详细设计（关键代码如下,具体参见附件中的源代码CompletionRoutine）</p><blockquote style="border-left-width: 1px; border-left-style: solid; border-left-color: #cccccc; margin-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; background-color: #fafafa; "><span class="p9" style="font-size: 9pt; text-decoration: none; "><pre style="font-family: monospace; font-size: 1em; "><span class="p9" style="font-size: 9pt; text-decoration: none; ">代码:<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " />
VOID&nbsp;WINAPI&nbsp;APC_A&nbsp;(&nbsp;DWORD&nbsp;dwError,&nbsp;DWORD&nbsp;cbTransferred,&nbsp;LPOVERLAPPED&nbsp;lpo&nbsp;)
{
&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"执行IO_A的完成例程"&nbsp;)&nbsp;;
}
VOID&nbsp;WINAPI&nbsp;APC_B&nbsp;(&nbsp;DWORD&nbsp;dwError,&nbsp;DWORD&nbsp;cbTransferred,&nbsp;LPOVERLAPPED&nbsp;lpo&nbsp;)
{
&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"执行IO_B的完成例程"&nbsp;)&nbsp;;
}
VOID&nbsp;WINAPI&nbsp;APC_C&nbsp;(&nbsp;DWORD&nbsp;dwError,&nbsp;DWORD&nbsp;cbTransferred,&nbsp;LPOVERLAPPED&nbsp;lpo&nbsp;)
{
&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"执行IO_C的完成例程"&nbsp;)&nbsp;;
}

void&nbsp;CCompletionRoutineDlg::OnTest()&nbsp;
{
&nbsp;&nbsp;//&nbsp;TODO:&nbsp;Add&nbsp;your&nbsp;control&nbsp;notification&nbsp;handler&nbsp;code&nbsp;here
&nbsp;&nbsp;HANDLE&nbsp;&nbsp;&nbsp;&nbsp;hFile_A,&nbsp;hFile_B,&nbsp;hFile_C&nbsp;;
&nbsp;&nbsp;OVERLAPPED&nbsp;&nbsp;ov_A&nbsp;=&nbsp;{0},&nbsp;ov_B&nbsp;=&nbsp;{0},&nbsp;ov_C&nbsp;=&nbsp;{0}&nbsp;;

#define&nbsp;C_SIZE&nbsp;1024&nbsp;*&nbsp;1024&nbsp;*&nbsp;32

&nbsp;&nbsp;string&nbsp;&nbsp;szText_A&nbsp;=&nbsp;"Sample&nbsp;A&nbsp;!"&nbsp;;
&nbsp;&nbsp;string&nbsp;&nbsp;szText_B&nbsp;=&nbsp;"Sampel&nbsp;B&nbsp;!"&nbsp;;
&nbsp;&nbsp;string&nbsp;&nbsp;szText_C&nbsp;;
&nbsp;&nbsp;szText_C.resize&nbsp;(&nbsp;C_SIZE&nbsp;)&nbsp;;
&nbsp;&nbsp;memset&nbsp;(&nbsp;&amp;(szText_C[0]),&nbsp;0x40,&nbsp;C_SIZE&nbsp;)&nbsp;;
&nbsp;&nbsp;
&nbsp;&nbsp;pTempInfo.clear&nbsp;()&nbsp;;

&nbsp;&nbsp;hFile_A&nbsp;=&nbsp;CreateFile&nbsp;(&nbsp;"A.txt",&nbsp;GENERIC_WRITE,&nbsp;0,&nbsp;NULL,&nbsp;\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE_ALWAYS,&nbsp;FILE_FLAG_OVERLAPPED,&nbsp;NULL&nbsp;)&nbsp;;
&nbsp;&nbsp;hFile_B&nbsp;=&nbsp;CreateFile&nbsp;(&nbsp;"B.txt",&nbsp;GENERIC_WRITE,&nbsp;0,&nbsp;NULL,&nbsp;\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE_ALWAYS,&nbsp;FILE_FLAG_OVERLAPPED,&nbsp;NULL&nbsp;)&nbsp;;
&nbsp;&nbsp;hFile_C&nbsp;=&nbsp;CreateFile&nbsp;(&nbsp;"C.txt",&nbsp;GENERIC_WRITE,&nbsp;0,&nbsp;NULL,&nbsp;\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE_ALWAYS,&nbsp;FILE_FLAG_OVERLAPPED,&nbsp;NULL&nbsp;)&nbsp;;

&nbsp;&nbsp;WriteFileEx&nbsp;(&nbsp;hFile_A,&nbsp;&amp;(szText_A[0]),&nbsp;szText_A.length(),&nbsp;&amp;ov_A,&nbsp;APC_A&nbsp;)&nbsp;;
&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"启动IO_A,&nbsp;并立即返回"&nbsp;)&nbsp;;

&nbsp;&nbsp;WriteFileEx&nbsp;(&nbsp;hFile_B,&nbsp;&amp;(szText_B[0]),&nbsp;szText_B.length(),&nbsp;&amp;ov_B,&nbsp;APC_B&nbsp;)&nbsp;;
&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"启动IO_B,&nbsp;并立即返回"&nbsp;)&nbsp;;

&nbsp;&nbsp;WriteFileEx&nbsp;(&nbsp;hFile_C,&nbsp;&amp;(szText_C[0]),&nbsp;szText_C.size(),&nbsp;&amp;ov_C,&nbsp;APC_C&nbsp;)&nbsp;;
&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"启动IO_C,&nbsp;并立即返回"&nbsp;)&nbsp;;

&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"进入可变等待状态"&nbsp;)&nbsp;;
&nbsp;&nbsp;SleepEx&nbsp;(&nbsp;1,&nbsp;true&nbsp;)&nbsp;;
&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"结束可变等待状态"&nbsp;)&nbsp;;

&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"进入可变等待状态"&nbsp;)&nbsp;;
&nbsp;&nbsp;SleepEx&nbsp;(&nbsp;10000,&nbsp;true&nbsp;)&nbsp;;
&nbsp;&nbsp;pTempInfo.push_back&nbsp;(&nbsp;"结束可变等待状态"&nbsp;)&nbsp;;

&nbsp;&nbsp;CloseHandle&nbsp;(&nbsp;hFile_A&nbsp;)&nbsp;;
&nbsp;&nbsp;CloseHandle&nbsp;(&nbsp;hFile_B&nbsp;)&nbsp;;
&nbsp;&nbsp;CloseHandle&nbsp;(&nbsp;hFile_C&nbsp;)&nbsp;;

&nbsp;&nbsp;m_ListBox.ResetContent&nbsp;()&nbsp;;
&nbsp;&nbsp;
&nbsp;&nbsp;list&lt;string&gt;::iterator&nbsp;p&nbsp;;
&nbsp;&nbsp;for&nbsp;(&nbsp;p&nbsp;=&nbsp;pTempInfo.begin();&nbsp;p&nbsp;!=&nbsp;pTempInfo.end();&nbsp;p++&nbsp;)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;m_ListBox.AddString&nbsp;(&nbsp;p-&gt;data()&nbsp;)&nbsp;;
&nbsp;&nbsp;}

&nbsp;&nbsp;DeleteFile&nbsp;(&nbsp;"A.txt"&nbsp;)&nbsp;;
&nbsp;&nbsp;DeleteFile&nbsp;(&nbsp;"B.txt"&nbsp;)&nbsp;;
&nbsp;&nbsp;DeleteFile&nbsp;(&nbsp;"C.txt"&nbsp;)&nbsp;;
}
<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " /></span></pre><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; ">&nbsp;</p></span></blockquote><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; "><br />执行后的效果如下（WinXP+SP2+VC6.0）：<br />&nbsp;<img src="http://www.cppblog.com/images/cppblog_com/flyinghare/201106304.gif" width="201" height="283" alt="" /><br /><br />4、&nbsp;&nbsp;心得体会<br />每当一个IO操作结束时会产生一个完成信息，如果该IO操作有完成例程的话就添加到完成例程队列。一旦调用线程进入可变等待状态，就会依次执行队列中的完成例程。<br />在这个示例中还有一个问题，如果把这个软件放在系统分区的文件目录下可以正常执行，而放在其他盘符下就会出现问题，执行结果就不同，真是奇怪了。<br /><br /><br />四、使用完成端口（IOCP）<br /><br />实例二、使用IOCP的异步I/O示例<br />1、设计目标<br />体会完成端口的异步I/O实现原理及过程。<br /><br />2、&nbsp;&nbsp;问题的分析与设计<br /><img src="http://www.cppblog.com/images/cppblog_com/flyinghare/201106305.gif" width="533" height="385" alt="" /><br /><br />说明：<br />&nbsp;&nbsp;每个客户端与一个管道进行交互，而在交互过程中I/O操作结束后产生的完成包就会进入&#8220;I/O完成包队列&#8221;。完成端口的线程队列中的线程使用GetQueuedCompletionStatus来检测&#8220;I/O完成包队列&#8221;中是否有完成包信息。&nbsp;<br />3、详细设计（关键代码如下，具体见附件中的源码）</p><blockquote style="border-left-width: 1px; border-left-style: solid; border-left-color: #cccccc; margin-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; background-color: #fafafa; "><span class="p9" style="font-size: 9pt; text-decoration: none; "><pre style="font-family: monospace; font-size: 1em; "><span class="p9" style="font-size: 9pt; text-decoration: none; ">代码:<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " />
UINT&nbsp;ServerThread&nbsp;(&nbsp;LPVOID&nbsp;lpParameter&nbsp;)
{
&nbsp;&nbsp;&#8230;&#8230;
&nbsp;&nbsp;while&nbsp;(&nbsp;true&nbsp;)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;GetQueuedCompletionStatus&nbsp;(&nbsp;pMyDlg-&gt;hCompletionPort,&nbsp;&amp;cbTrans,&nbsp;&amp;dwCompletionKey,&nbsp;&amp;lpov,&nbsp;INFINITE&nbsp;)&nbsp;;
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(&nbsp;dwCompletionKey&nbsp;==&nbsp;-1&nbsp;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break&nbsp;;
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;读取管道信息
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;响应管道信息（写入）
&nbsp;&nbsp;}
&nbsp;&nbsp;return&nbsp;0&nbsp;;
}

void&nbsp;CMyDlg::OnStart()&nbsp;
{
&nbsp;&nbsp;//&nbsp;创建完成端口
&nbsp;&nbsp;hCompletionPort&nbsp;=&nbsp;CreateIoCompletionPort&nbsp;(&nbsp;INVALID_HANDLE_VALUE,&nbsp;NULL,&nbsp;0,&nbsp;nMaxThread&nbsp;)&nbsp;;

&nbsp;&nbsp;CString&nbsp;lpPipeName&nbsp;=&nbsp;"\\\\.\\Pipe\\NamedPipe"&nbsp;;
&nbsp;&nbsp;for&nbsp;(&nbsp;UINT&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nMaxPipe;&nbsp;i++&nbsp;)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;创建命名管道
&nbsp;&nbsp;&nbsp;&nbsp;PipeInst[i].hPipe&nbsp;=&nbsp;&nbsp;CreateNamedPipe&nbsp;(&nbsp;lpPipeName,&nbsp;PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,&nbsp;\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT,&nbsp;nMaxPipe,&nbsp;0,&nbsp;0,&nbsp;INFINITE,&nbsp;NULL&nbsp;)&nbsp;;
&nbsp;&nbsp;&nbsp;&nbsp;&#8230;&#8230;
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;把命名管道与完成端口关联起来
&nbsp;&nbsp;&nbsp;&nbsp;HANDLE&nbsp;hRet&nbsp;=&nbsp;CreateIoCompletionPort&nbsp;(&nbsp;PipeInst[i].hPipe,&nbsp;hCompletionPort,&nbsp;i,&nbsp;nMaxThread&nbsp;)&nbsp;;
&nbsp;&nbsp;&nbsp;&nbsp;&#8230;&#8230;
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;等待连接
&nbsp;&nbsp;&nbsp;&nbsp;ConnectNamedPipe&nbsp;(&nbsp;PipeInst[i].hPipe,&nbsp;&amp;(PipeInst[i].ov)&nbsp;)&nbsp;;
&nbsp;&nbsp;}
&nbsp;&nbsp;//&nbsp;创建线程
&nbsp;&nbsp;for&nbsp;(&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nMaxThread;&nbsp;i++&nbsp;)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;hThread[i]&nbsp;=&nbsp;AfxBeginThread&nbsp;(&nbsp;ServerThread,&nbsp;NULL,&nbsp;THREAD_PRIORITY_NORMAL&nbsp;)&nbsp;;
&nbsp;&nbsp;}
&nbsp;&nbsp;&#8230;&#8230;
}
void&nbsp;CMyDlg::OnStop()&nbsp;
{
&nbsp;&nbsp;for&nbsp;(&nbsp;UINT&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nMaxThread;&nbsp;i++&nbsp;)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;用来唤醒线程的虚假I/O完成包
&nbsp;&nbsp;&nbsp;&nbsp;PostQueuedCompletionStatus&nbsp;(&nbsp;hCompletionPort,&nbsp;0,&nbsp;-1,&nbsp;NULL&nbsp;)&nbsp;;
&nbsp;&nbsp;&nbsp;&nbsp;CloseHandle&nbsp;(&nbsp;hThread[i]&nbsp;)&nbsp;;
&nbsp;&nbsp;}
&nbsp;&nbsp;for&nbsp;(&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nMaxPipe;&nbsp;i++&nbsp;)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;DisconnectNamedPipe&nbsp;(&nbsp;PipeInst[i].hPipe&nbsp;)&nbsp;;
&nbsp;&nbsp;&nbsp;&nbsp;CloseHandle&nbsp;(&nbsp;PipeInst[i].hPipe&nbsp;)&nbsp;;
&nbsp;&nbsp;}
&nbsp;&nbsp;&#8230;&#8230;
}
<hr style="height: 1px; margin-top: 1.5em; margin-right: 10px; margin-bottom: 1.5em; margin-left: 10px; border-bottom-color: black; border-bottom-width: thin; border-bottom-style: initial; " /></span></pre><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; ">&nbsp;</p></span></blockquote><p class="p9" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-size: 9pt; text-decoration: none; "><br />4、心得体会<br />&nbsp;&nbsp;上面这个例子是关于完成端口的简单应用。可以这样来理解完成端口，它与三种资源相关分别是管道、I/O完成包队列、线程队列，它的作用是协调这三种资源。<br />【参考文献】<br />[1].&nbsp;Windows系统编程.&nbsp;Johnson&nbsp;M.Hart著<br />【版权声明】必须注明来源看雪技术论坛(bbs.pediy.com)&nbsp;及作者，并保持文章的完整性。</p></span><br />转自：<a href="http://andylin02.iteye.com/blog/476399">http://andylin02.iteye.com/blog/476399</a><img src ="http://www.cppblog.com/flyinghare/aggbug/149714.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2011-06-29 10:13 <a href="http://www.cppblog.com/flyinghare/archive/2011/06/29/149714.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>float和double类型的内存分布和比较方法</title><link>http://www.cppblog.com/flyinghare/archive/2010/08/27/124935.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Fri, 27 Aug 2010 05:31:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2010/08/27/124935.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/124935.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2010/08/27/124935.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/124935.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/124935.html</trackback:ping><description><![CDATA[<p>C/C++的浮点数据类型有float和double两种。<br>类型float大小为4字节，即32位，内存中的存储方式如下：<br>&nbsp;符号位（1 bit） 指数（8 bit） 尾数（23 bit） </p>
<p><br>类型double大小为8字节，即64位，内存布局如下：<br>符号位（1 bit） 指数（11 bit） 尾数（52 bit） </p>
<p><br>&nbsp;<br>符号位决定浮点数的正负，0正1负。<br>指数和尾数均从浮点数的二进制科学计数形式中获取。<br>如，十进制浮点数2.5的二进制形式为10.1，转换为科学计数法形式为(1.01)*(10^1)，由此可知指数为1，尾数（即科学计数法的小数部分）为01。<br>根据浮点数的存储标准（IEEE制定），float类型指数的起始数为127（二进制0111 1111），double类型指数的起始数为1023(二进制011 1111 1111)，在此基础上加指数，得到的就是内存中指数的表示形式。尾数则直接填入，如果空间多余则以0补齐，如果空间不够则0舍1入。所以float和double类型分别表示的2.5如下（二进制）：<br>符号位 指数 尾数 <br>0 1000 0000 010 0000 0000 0000 0000 0000 <br>0 100 0000 0000 0100 0000 0000 0000 0000 0000 0000 <br>0000 0000 0000 0000 0000 0000 </p>
<p><br>浮点数2.5可以用二进制小数准确表示（2.5=1*(2^1)+0*(2^0)+1*(2^-1)），但很多小数不可以准确表示，其二进制形式的小数部分会无限循环，如浮点数-1.2表示如下（二进制）：<br>符号位 指数 尾数 <br>1 0111 1111 0011 0011 0011 0011 0011 010 <br>1 011 1111 1111 0011 0011 0011 0011 0011 0011 0011<br>0011 0011 0011 0011 0011 0011 </p>
<p><br>由于对无限循环尾数的截取遵循0舍1入，尾数的第21~24位为0011，第53~56位为0011，而float尾数容量为23位，double尾数容量为52位，所以，float形式的最后三位因进位而成010，double形式则没有进位发生。<br>&nbsp;<br>类型float和double通过==,&gt;,&lt;等比较不会引起编译错误，但是非常可能得到错误的结果。这是因为它们的内存分布不同，不可以直接比较。正确的方法是转换为同一类型后比较两者差值，如果结果小于规定的小值，则视为相等。<br>如，一个比较double的实现：<br><a href="http://metasharp.net/index.php?title=How_to_compare_double_or_float_in_Cpp">http://metasharp.net/index.php?title=How_to_compare_double_or_float_in_Cpp</a><br>另外，本文参考了如下webs：<br><a href="http://cdatatype.blogspot.com/2008/01/memory-map-of-floatdouble.html">http://cdatatype.blogspot.com/2008/01/memory-map-of-floatdouble.html</a><br><a href="http://blog.csdn.net/hzb1983/archive/2007/09/24/1798555.aspx">http://blog.csdn.net/hzb1983/archive/2007/09/24/1798555.aspx</a><br>&nbsp;<br>P.S.<br>1)<br>IEEE浮点数标准：&nbsp;&nbsp;&nbsp;&nbsp; 4字节浮点数：1位符号位，8位阶数（基数为127的移码），23位尾数；&nbsp;&nbsp;&nbsp;&nbsp; 8字节浮点数：1位符号位，11位阶数（基数为1023的移码），52位尾数<br>2 )<br>在VC中： float数值范围约在 -10e38~10e38，并提供7位有效数字位，绝对值小于10e38地数被处理成零值 double数值范围约在-10e308~10e308，并提供15~16位有效数字，绝对值小于10e308地数被处理成零值</p>
<p><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/vinsendai/archive/2008/06/27/2593035.aspx">http://blog.csdn.net/vinsendai/archive/2008/06/27/2593035.aspx</a></p>
<img src ="http://www.cppblog.com/flyinghare/aggbug/124935.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2010-08-27 13:31 <a href="http://www.cppblog.com/flyinghare/archive/2010/08/27/124935.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函數調用方式： Stdcall Cdecl Fastcall WINAPI CALLBACK PASCAL Thiscall Fortran Syscall Declspec(Naked)</title><link>http://www.cppblog.com/flyinghare/archive/2010/07/02/119163.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Fri, 02 Jul 2010 06:07:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2010/07/02/119163.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/119163.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2010/07/02/119163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/119163.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/119163.html</trackback:ping><description><![CDATA[
<span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">&nbsp;&nbsp; &nbsp; &nbsp; 现代的编程语言的函数竟然有那麽多的调用方式。这些东西要完全理解还得通过汇编代码才好理解。他们各自有自己的特点</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">其实这些调用方式的差别在主要在一下几个方面</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">1.参数处理方式（传递顺序，存取(利用盏还是寄存器)）</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">2.函数的结尾处理方式（善后处理）</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">以下是理论：</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><pre>__cdecl    由调用者平栈，参数从右到左依次入栈 是C和C＋＋程序的缺省调用方式。<strong>每一个调用它的函数都包含清空堆栈的代码</strong>，<br>           所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上<br>           下划线前缀。是MFC缺省调用约定<br>__stdcall ，WINAPI，CALLBACK ，PASCAL    由被调用者平栈，参数从右到左依次入栈 ._stdcall是Pascal程序的缺省调用方式，<br>           通常用于Win32&nbsp;&nbsp; Api中，函数采用从右到左的压栈方式，<strong>自己在退出时清空堆栈</strong>。VC将函数编译后会在函数名前面加上下划<br>           线前缀，在函数名后加上"@"和参数的字节数<br><br>__fastcall 由被调用者平栈，参数先赋值给寄存器，然后入栈  &#8220;人&#8221;如其名，它的主要特点就是快，因为它是通过寄存器来传送参数的<br>          （实际上，它<strong>用ECX和EDX传送前两个双字（DWORD）或更小的参数</strong>，剩下的参数仍旧自右向左压栈传送，被调用的函数在返回前<br>          清理传送参数的内存栈），在函数名修饰约定方面，它和前两者均不同.<br>          _fastcall方式的函数采用寄存器传递参数，VC将函数编译后会在函数名前面加上"@"前缀，在函数名后加上"@"和参数的字节数。&nbsp; <br><br>__thiscall 由被调用者平栈，参数入栈，this 指针赋给 ecx 寄存器 仅仅应用于&#8220;C++&#8221;成员函数。this指针存放于CX寄存器，参数从右<br>           到左压。thiscall不是关键词，因此不能被程序员指定。&nbsp;&nbsp; <br><br><br><span style="color: rgb(0, 0, 0); ">__declspec(naked) </span><span style="font-size: 12pt; font-family: 宋体; ">这是一个很少见的调用约定，一般程序设计者建议不要使用。<strong>编译器不会给这种函数增加初始化和清理代码</strong>，<br>          更特殊的是，你不能用<span lang="EN-US">return</span>返回返回值，只能用插入汇编返回结果。这一般用于实模式驱动程序设计.<br></span></pre></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">以下是实践：</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><font face="宋体" size="2"><font face="宋体" size="2"></font></font></span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">&nbsp;</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">&nbsp;</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><div style="border-left-color: rgb(204, 204, 204); padding-top: 4px; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 1634px; font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; "><span style="color: rgb(0, 0, 255); ">int</span><span style="color: rgb(0, 0, 0); ">&nbsp;__stdcall&nbsp;test_stdcall(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para1,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para2)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;para1&nbsp;</span><span style="color: rgb(0, 0, 0); ">=</span><span style="color: rgb(0, 0, 0); ">&nbsp;para2;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">return</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">0</span><span style="color: rgb(0, 0, 0); ">;<br>}<br></span><span style="color: rgb(0, 0, 255); ">int</span><span style="color: rgb(0, 0, 0); ">&nbsp;__cdecl&nbsp;test_cdecl(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para,&nbsp;<img src="http://www.cnitblog.com/Images/dot.gif">)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: rgb(0, 0, 0); ">=</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">\n</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">;<br>&nbsp;&nbsp;&nbsp;&nbsp;va_list&nbsp;marker;<br>&nbsp;&nbsp;&nbsp;&nbsp;va_start(&nbsp;marker,&nbsp;para&nbsp;);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">while</span><span style="color: rgb(0, 0, 0); ">(&nbsp;p&nbsp;</span><span style="color: rgb(0, 0, 0); ">!=</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">\0</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: rgb(0, 0, 0); ">=</span><span style="color: rgb(0, 0, 0); ">&nbsp;va_arg(&nbsp;marker,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="color: rgb(0, 0, 0); ">"</span><span style="color: rgb(0, 0, 0); ">%c\n</span><span style="color: rgb(0, 0, 0); ">"</span><span style="color: rgb(0, 0, 0); ">,&nbsp;p);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;va_end(&nbsp;marker&nbsp;);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">return</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">0</span><span style="color: rgb(0, 0, 0); ">;<br>}<br><br></span><span style="color: rgb(0, 0, 255); ">int</span><span style="color: rgb(0, 0, 0); ">&nbsp;pascal&nbsp;test_pascal(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para1,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para2)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">return</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">0</span><span style="color: rgb(0, 0, 0); ">;<br>}<br><br></span><span style="color: rgb(0, 0, 255); ">int</span><span style="color: rgb(0, 0, 0); ">&nbsp;__fastcall&nbsp;test_fastcall(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para1,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para2,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para3,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para4)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;para1&nbsp;</span><span style="color: rgb(0, 0, 0); ">=</span><span style="color: rgb(0, 0, 0); ">&nbsp;(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">)</span><span style="color: rgb(0, 0, 0); ">1</span><span style="color: rgb(0, 0, 0); ">;<br>&nbsp;&nbsp;&nbsp;&nbsp;para2&nbsp;</span><span style="color: rgb(0, 0, 0); ">=</span><span style="color: rgb(0, 0, 0); ">&nbsp;(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">)</span><span style="color: rgb(0, 0, 0); ">2</span><span style="color: rgb(0, 0, 0); ">;<br>&nbsp;&nbsp;&nbsp;&nbsp;para3&nbsp;</span><span style="color: rgb(0, 0, 0); ">=</span><span style="color: rgb(0, 0, 0); ">&nbsp;(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">)</span><span style="color: rgb(0, 0, 0); ">3</span><span style="color: rgb(0, 0, 0); ">;<br>&nbsp;&nbsp;&nbsp;&nbsp;para4&nbsp;</span><span style="color: rgb(0, 0, 0); ">=</span><span style="color: rgb(0, 0, 0); ">&nbsp;(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">)</span><span style="color: rgb(0, 0, 0); ">4</span><span style="color: rgb(0, 0, 0); ">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">return</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">0</span><span style="color: rgb(0, 0, 0); ">;<br>}<br>__declspec(naked)&nbsp;</span><span style="color: rgb(0, 0, 255); ">void</span><span style="color: rgb(0, 0, 0); ">&nbsp;__stdcall&nbsp;test_naked(</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para1,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">&nbsp;para2)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;__asm<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;ebp<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ebp,&nbsp;esp<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;eax<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;al,</span><span style="color: rgb(0, 0, 255); ">byte</span><span style="color: rgb(0, 0, 0); ">&nbsp;ptr&nbsp;[ebp&nbsp;</span><span style="color: rgb(0, 0, 0); ">+</span><span style="color: rgb(0, 0, 0); ">&nbsp;0Ch]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xchg&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">byte</span><span style="color: rgb(0, 0, 0); ">&nbsp;ptr&nbsp;[ebp&nbsp;</span><span style="color: rgb(0, 0, 0); ">+</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">8</span><span style="color: rgb(0, 0, 0); ">],al&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;eax<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ebp<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0); ">8</span><span style="color: rgb(0, 0, 0); "><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="color: rgb(0, 128, 0); ">//</span><span style="color: rgb(0, 128, 0); ">&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;;</span><span style="color: rgb(0, 128, 0); "></span><span style="color: rgb(0, 0, 0); "><br>}<br><br><br></span><span style="color: rgb(0, 0, 255); ">int</span><span style="color: rgb(0, 0, 0); ">&nbsp;main(</span><span style="color: rgb(0, 0, 255); ">int</span><span style="color: rgb(0, 0, 0); ">&nbsp;argc,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">*</span><span style="color: rgb(0, 0, 0); ">&nbsp;argv[])<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;test_stdcall(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">a</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">b</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;);<br>&nbsp;&nbsp;&nbsp;&nbsp;test_cdecl(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">c</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">d</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">e</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">f</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">g</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">h</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">\0</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">);<br>&nbsp;&nbsp;&nbsp;&nbsp;test_pascal(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">e</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">f</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;);<br>&nbsp;&nbsp;&nbsp;&nbsp;test_fastcall(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">g</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">h</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">i</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">j</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;);<br>&nbsp;&nbsp;&nbsp;&nbsp;test_naked(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">k</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">l</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">return</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">0</span><span style="color: rgb(0, 0, 0); ">;<br>}</span></div><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; ">汇编代码如下</span><span style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; "><br></span><div style="border-left-color: rgb(204, 204, 204); padding-top: 4px; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 1634px; font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; "><span style="color: rgb(0, 0, 255); ">int</span><span style="color: rgb(0, 0, 0); ">&nbsp;main(</span><span style="color: rgb(0, 0, 255); ">int</span><span style="color: rgb(0, 0, 0); ">&nbsp;argc,&nbsp;</span><span style="color: rgb(0, 0, 255); ">char</span><span style="color: rgb(0, 0, 0); ">*</span><span style="color: rgb(0, 0, 0); ">&nbsp;argv[])<br>{<br></span><span style="color: rgb(0, 0, 0); ">00411350</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ebp&nbsp;&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411351</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ebp,esp&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411353</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;esp,0C0h&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411359</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ebx&nbsp;&nbsp;<br>0041135A&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;esi&nbsp;&nbsp;<br>0041135B&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;edi&nbsp;&nbsp;<br>0041135C&nbsp;&nbsp;lea&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;edi,[ebp</span><span style="color: rgb(0, 0, 0); ">-</span><span style="color: rgb(0, 0, 0); ">0C0h]&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411362</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ecx,30h&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411367</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;eax,0CCCCCCCCh&nbsp;<br>0041136C&nbsp;&nbsp;rep&nbsp;stos&nbsp;&nbsp;&nbsp;&nbsp;dword&nbsp;ptr&nbsp;es:[edi]&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;test_stdcall(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">a</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">b</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;);<br><strong><font color="#000000">0041136E&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;62h&nbsp;&nbsp;<br></font></strong></span><strong><font color="#000000"><span style="color: rgb(0, 0, 0); ">00411370</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;61h&nbsp;&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411372</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font><font color="#000000"><span style="color: rgb(0, 0, 0); ">_test_stdcall@</span><span style="color: rgb(0, 0, 0); ">8</span></font><span style="color: rgb(0, 0, 0); "></span></strong><span style="color: rgb(0, 0, 0); "><strong><font color="#000000"></font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;test_cdecl(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">c</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">d</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">e</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">f</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">g</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">h</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;,</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">\0</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">);<br></span><strong><span style="color: rgb(0, 0, 0); ">00411377</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0); ">0</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411379</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;68h&nbsp;&nbsp;<br>0041137B&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;67h&nbsp;&nbsp;<br>0041137D&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;66h&nbsp;&nbsp;<br>0041137F&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;65h&nbsp;&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411381</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;64h&nbsp;&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411383</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;63h&nbsp;&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411385</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0); ">_test_cdecl</span></strong><span style="color: rgb(0, 0, 0); "><br><font color="#ff0000"><strong>0041138A&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;esp,1Ch&nbsp;</strong></font></span><font color="#ff0000"><strong><font color="#ff0000"><span style="color: rgb(0, 128, 0); ">;恢复</span></font><span style="color: rgb(0, 0, 0); "></span><span style="color: rgb(0, 0, 0); ">_te</span></strong><strong><span style="color: rgb(0, 0, 0); ">st_cdecl</span><span style="color: rgb(0, 0, 0); ">参数压入前的堆栈指令是:&nbsp;</span><span style="color: rgb(0, 0, 0); ">add esp,n*4&nbsp;</span>n=参数的数量</strong></font><br><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;&nbsp; test_fastcall(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">g</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">h</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">i</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">j</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">&nbsp;);<br><strong>0041138D&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6Ah&nbsp;&nbsp;<br>0041138F&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;69h&nbsp;&nbsp;<br></strong></span><strong><span style="color: rgb(0, 0, 0); ">00411391</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dl,68h&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411393</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl,67h&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">00411395</span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0); ">test_fastcall</span></strong><span style="color: rgb(0, 0, 0); "><br>&nbsp;&nbsp;&nbsp;&nbsp;test_naked(&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">k</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">,&nbsp;</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">l</span><span style="color: rgb(0, 0, 0); ">'</span><span style="color: rgb(0, 0, 0); ">);<br><strong>0041139A&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6Ch&nbsp;&nbsp;<br>0041139C&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6Bh&nbsp;&nbsp;<br>0041139E&nbsp;&nbsp;call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</strong></span><strong><span style="color: rgb(0, 0, 0); ">_test_naked</span></strong><span style="color: rgb(0, 0, 0); "><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255); ">return</span><span style="color: rgb(0, 0, 0); ">&nbsp;</span><span style="color: rgb(0, 0, 0); ">0</span><span style="color: rgb(0, 0, 0); ">;<br>004113A3&nbsp;&nbsp;xor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;eax,eax&nbsp;<br>}<br><br>int __stdcall test_stdcall(char para1, char para2)<br>{<br>004111F0&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;<br>004111F1&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp,esp&nbsp;<br>004111F3&nbsp; sub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,0C0h&nbsp;<br>004111F9&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebx&nbsp;&nbsp;<br>004111FA&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esi&nbsp;&nbsp;<br>004111FB&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edi&nbsp;&nbsp;<br>004111FC&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edi,[ebp-0C0h]&nbsp;<br>00411202&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,30h&nbsp;<br>00411207&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,0CCCCCCCCh&nbsp;<br>0041120C&nbsp; rep stos&nbsp;&nbsp;&nbsp; dword ptr es:[edi] ;初始edi<br>&nbsp;&nbsp;&nbsp; para1 = para2;<br><font color="#ff0000"><strong>0041120E&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; al,byte ptr [para2] ;mov al,byte ptr[ebp+c]<br>00411211&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [para1],al ;mov byte ptr[ebp+8],al</strong></font><br>&nbsp;&nbsp;&nbsp; return 0;<br>00411214&nbsp; xor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,eax<br>00411216&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edi&nbsp;&nbsp;<br>00411217&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esi&nbsp;&nbsp;<br>00411218&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebx&nbsp;&nbsp;<br>00411219&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,ebp&nbsp;<br>0041121B&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;<br><font color="#ff0000"><strong>0041121C ret&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8 ;恢复到压入函数参数前堆栈,由于有两个参数所以ret 8 相当于 pop eip 然后esp+8</strong></font><br>}<br>int __cdecl test_cdecl(char para,... )<br>{<br>00411230&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;<br>00411231&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp,esp&nbsp;<br>00411233&nbsp; sub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,0D8h&nbsp;<br>0041123C&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edi,[ebp-0D8h]&nbsp;<br>00411242&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,36h&nbsp;<br>00411247&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,0CCCCCCCCh&nbsp;<br>0041124C&nbsp; rep stos&nbsp;&nbsp;&nbsp; dword ptr es:[edi]&nbsp;<br>&nbsp;&nbsp;&nbsp; char&nbsp;&nbsp;&nbsp; p = '\n';<br>0041124E&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [p],0Ah&nbsp;<br>&nbsp;&nbsp;&nbsp; va_list marker;<br>&nbsp;&nbsp;&nbsp; va_start( marker, para );<br>00411252&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,[ebp+0Ch]&nbsp;<br>00411255&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [marker],eax&nbsp;<br>&nbsp;&nbsp;&nbsp; while( p != '\0' )<br>00411258&nbsp; movsx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,byte ptr [p]&nbsp;<br>0041125C&nbsp; test&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,eax&nbsp;<br>0041125E&nbsp; je&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test_cdecl+60h (411290h)&nbsp;<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p = va_arg( marker, char);<br>00411260&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,dword ptr [marker]&nbsp;<br>00411263&nbsp; add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,4&nbsp;<br>00411266&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [marker],eax&nbsp;<br>00411269&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,dword ptr [marker]&nbsp;<br>0041126C&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dl,byte ptr [ecx-4]&nbsp;<br>0041126F&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [p],dl&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%c\n", p);<br>00411272&nbsp; movsx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,byte ptr [p]&nbsp;<br>00411276&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esi,esp&nbsp;<br>00411278&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;<br>00411279&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset string "%c\n" (41401Ch)&nbsp;<br>0041127E&nbsp; call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [__imp__printf (416180h)]&nbsp;<br>00411284&nbsp; add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,8&nbsp;<br>0041128E&nbsp; jmp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test_cdecl+28h (411258h)&nbsp;<br></span><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;&nbsp; }</span><br><span style="color: rgb(0, 0, 0); ">&nbsp;&nbsp;&nbsp; va_end( marker );<br>00411290&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [marker],0&nbsp;<br>&nbsp;&nbsp;&nbsp; return 0;<br>00411297&nbsp; xor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,eax&nbsp;</span><span style="color: rgb(0, 0, 0); "><br>004112A9&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,ebp&nbsp;<br>004112AB&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;<br>004112AC&nbsp; ret&nbsp;&nbsp;&nbsp;&nbsp;</span><br><span style="color: rgb(0, 0, 0); ">}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>int __fastcall test_fastcall(char para1, char para2, char para3, char para4)<br>{<br>004112D0&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;<br>004112D1&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp,esp&nbsp;<br>004112D3&nbsp; sub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,0D8h&nbsp;&nbsp;<br>004112DD&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edi,[ebp-0D8h]&nbsp;<br>004112E3&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,36h&nbsp;<br>004112E8&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,0CCCCCCCCh&nbsp;<br>004112ED&nbsp; rep stos&nbsp;&nbsp;&nbsp; dword ptr es:[edi]&nbsp;<br>004112EF&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx&nbsp;&nbsp;<br>004112F0&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [ebp-14h],dl&nbsp;<br>004112F3&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [ebp-8],cl&nbsp;<br>&nbsp;&nbsp;&nbsp; para1 = (char)1;<br>004112F6&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [para1],1&nbsp;<br>&nbsp;&nbsp;&nbsp; para2 = (char)2;<br>004112FA&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [para2],2&nbsp;<br>&nbsp;&nbsp;&nbsp; para3 = (char)3;<br>004112FE&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [para3],3&nbsp;<br>&nbsp;&nbsp;&nbsp; para4 = (char)4;<br>00411302&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte ptr [para4],4&nbsp;<br>&nbsp;&nbsp;&nbsp; return 0;<br>00411306&nbsp; xor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,eax&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0); "><br>0041130B&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,ebp&nbsp;<br>0041130D&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;<br>0041130E&nbsp; ret&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8&nbsp;&nbsp;</span><strong>;由于使用了ecx ,edx 传递参数 本来4个参数只使用两push 所以这里是 ret 4*2</strong><br><span style="color: rgb(0, 0, 0); ">}<br>&nbsp; &nbsp;&nbsp;&nbsp;<br><br>__declspec(naked) void __stdcall test_naked(char para1, char para2)<br>{<br>00411330&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;<strong>这里编译器没加入任何初始化和清栈的指令,你代码如何写它就复制过来</strong><br>00411331&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp,esp&nbsp;<br>00411333&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;<br>00411334&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; al,byte ptr [para2] &nbsp;&nbsp;&nbsp;<br>00411337&nbsp; xchg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; al,byte ptr [para1]&nbsp;<br>0041133A&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;<br>0041133B&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;<br>0041133C&nbsp; ret&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8&nbsp;&nbsp;<br>}&nbsp;<br></span></div><div><span style="color: rgb(0, 0, 0); ">转自：</span><a href="http://www.cnitblog.com/textbox/archive/2010/03/10/64575.html">http://www.cnitblog.com/textbox/archive/2010/03/10/64575.html</a></div><img src ="http://www.cppblog.com/flyinghare/aggbug/119163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2010-07-02 14:07 <a href="http://www.cppblog.com/flyinghare/archive/2010/07/02/119163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于FS寄存器的资料 </title><link>http://www.cppblog.com/flyinghare/archive/2010/07/02/119160.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Fri, 02 Jul 2010 05:48:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2010/07/02/119160.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/119160.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2010/07/02/119160.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/119160.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/119160.html</trackback:ping><description><![CDATA[<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 1em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; font-family: verdana, sans-serif; font-size: 14px; line-height: 21px; "><font face="宋体">FS寄存器指向当前活动线程的TEB结构（线程结构）<br>偏移&nbsp; 说明<br>000&nbsp; 指向SEH链指针<br>004&nbsp; 线程堆栈顶部<br>008&nbsp; 线程堆栈底部<br>00C&nbsp; SubSystemTib<br>010&nbsp; FiberData<br>014&nbsp; ArbitraryUserPointer<br>018&nbsp; FS段寄存器在内存中的镜像地址<br>020&nbsp; 进程PID<br>024&nbsp; 线程ID<br>02C&nbsp; 指向线程局部存储指针<br>030&nbsp; PEB结构地址（进程结构）<br>034&nbsp; 上个错误号</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 1em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; font-family: verdana, sans-serif; font-size: 14px; line-height: 21px; "><font face="宋体"><br>得到KERNEL32.DLL基址的方法<br>assume fs:nothing&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;打开FS寄存器<br>mov eax,fs:[30h]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;得到PEB结构地址<br>mov eax,[eax + 0ch]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;得到PEB_LDR_DATA结构地址<br>mov esi,[eax + 1ch]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;InInitializationOrderModuleList<br>lodsd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;得到KERNEL32.DLL所在LDR_MODULE结构的InInitializationOrderModuleList地址<br>mov edx,[eax + 8h]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;得到BaseAddress，既Kernel32.dll基址</font>&nbsp;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 1em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; font-family: verdana, sans-serif; font-size: 14px; line-height: 21px; ">转自：<span  style="font-family: 微软雅黑; line-height: normal; font-size: medium; "><a href="http://blog.csdn.net/xbin8/archive/2008/03/08/2158762.aspx">http://blog.csdn.net/xbin8/archive/2008/03/08/2158762.aspx</a></span></p>
<img src ="http://www.cppblog.com/flyinghare/aggbug/119160.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2010-07-02 13:48 <a href="http://www.cppblog.com/flyinghare/archive/2010/07/02/119160.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>dll模块卸载顺序导致的问题</title><link>http://www.cppblog.com/flyinghare/archive/2010/05/19/115811.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 19 May 2010 07:33:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2010/05/19/115811.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/115811.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2010/05/19/115811.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/115811.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/115811.html</trackback:ping><description><![CDATA[
<div><br></div><div>★ 问题现象：在析构函数中释放引用的 COM 指针，导致异常。</div><div>1、总是在一个固定地址报告异常。该地址的汇编码在程序退出前可见（有效），在异常出现时不可见（全为问号）。</div><div>2、设置断点后发现，调试器无法计算该指针表达式的值；</div><div>3、在内存窗口中查看该指针指向的内容，发现可视范围内全是问号（不可访问）</div><div>★ 原因、查找过程</div><div>1、在欲释放的指针所在的dll卸载的地方（CXxxxApp::ExitInstance()）设上断点，发现它在出错代码前就执行了！</div><div>2、因为出错代码处也是一个dll模块内，而且和出错指针对象所在的dll不是一个。据此，判断应该是，主模块（exe）在卸载dll时，先卸载了指针所在的dll（而且报了内存泄露），再卸载出错代码所在的dll，由于此时指针所在的模块已经释放，故内存失效！</div><div>★ 解决办法</div><div>1、对于dll模块的代码，绝对不要在析构函数或模块退出时访问另一个dll的内容！因为 dll 的卸载顺序是不可靠或很难管理的！（exe卸载的顺序是可靠的，exe必定是最后卸载）</div><div>2、如果要在结束时释放某些东西，应该为 dll 导出一个类似&#8216;Uninitialize()&#8217;的函数，由主模块（exe）主动调用！</div><div><br></div><img src ="http://www.cppblog.com/flyinghare/aggbug/115811.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2010-05-19 15:33 <a href="http://www.cppblog.com/flyinghare/archive/2010/05/19/115811.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一种实现Win32窗口过程函数（Window Procedure）的新方法</title><link>http://www.cppblog.com/flyinghare/archive/2010/05/08/114875.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Sat, 08 May 2010 12:26:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2010/05/08/114875.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/114875.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2010/05/08/114875.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/114875.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/114875.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自：http://blog.csdn.net/JerKii/archive/2006/04/07/654188.aspx#pass_thispointer_stack一种实现Win32窗口过程函数（Window Procedure）的新方法基于Thunk实现的类成员消息处理函数JERKII.SHANG (JERKII@HOTMAIL.COM)MAR.10th - 31st, 2006Window...&nbsp;&nbsp;<a href='http://www.cppblog.com/flyinghare/archive/2010/05/08/114875.html'>阅读全文</a><img src ="http://www.cppblog.com/flyinghare/aggbug/114875.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2010-05-08 20:26 <a href="http://www.cppblog.com/flyinghare/archive/2010/05/08/114875.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Windows API实现一个简单的文本输入框</title><link>http://www.cppblog.com/flyinghare/archive/2010/02/16/107918.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Tue, 16 Feb 2010 04:07:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2010/02/16/107918.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/107918.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2010/02/16/107918.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/107918.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/107918.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自：http://blog.csdn.net/skyever2100/archive/2008/11/13/3292480.aspx一、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 概述在Windows Form应用中，Windows界面系统通过消息与应用程序进行交互，每个窗口都有相应的消息处...&nbsp;&nbsp;<a href='http://www.cppblog.com/flyinghare/archive/2010/02/16/107918.html'>阅读全文</a><img src ="http://www.cppblog.com/flyinghare/aggbug/107918.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2010-02-16 12:07 <a href="http://www.cppblog.com/flyinghare/archive/2010/02/16/107918.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>.NET中的幕后英雄MSCOREE.dll</title><link>http://www.cppblog.com/flyinghare/archive/2009/11/25/101889.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 25 Nov 2009 02:33:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/11/25/101889.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/101889.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/11/25/101889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/101889.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/101889.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自：http://blogs.msdn.com/yizhang/archive/2007/11/05/net-mscoree-dll.aspx&nbsp;&nbsp;现在做.NET Framework的开发的朋友应该是越来越多了，但是可能并非人人都对MSCOREE.DLL非常了解。而事实上，毫不夸张地说，MSCOREE.DLL是.NET Framework中最为核心的DLL之一，没有这个DL...&nbsp;&nbsp;<a href='http://www.cppblog.com/flyinghare/archive/2009/11/25/101889.html'>阅读全文</a><img src ="http://www.cppblog.com/flyinghare/aggbug/101889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-11-25 10:33 <a href="http://www.cppblog.com/flyinghare/archive/2009/11/25/101889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>emms指令在MMX指令中的作用</title><link>http://www.cppblog.com/flyinghare/archive/2009/11/10/100650.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Tue, 10 Nov 2009 11:11:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/11/10/100650.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/100650.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/11/10/100650.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/100650.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/100650.html</trackback:ping><description><![CDATA[<p>转自：<a href="http://blog.csdn.net/psusong/archive/2009/01/08/3737047.aspx">http://blog.csdn.net/psusong/archive/2009/01/08/3737047.aspx</a><br><br>　　 MMX和SSE都是INTEL开发的基于SIMD(单指令多数据流)的技术。所谓单指令多数据流是指可以用一条指令可以完成多个数据的操作。虽然64位系统已经推出，但是我们大部分都是使用32位系统，所以如果要完成两个128位的相加运算，用普通32位指令很明显要执行4条相加指令，而基于64位的MMX指令只需要执行两次即可完成，更强大的SSE能一次处理128位，故一次就可以完成操作，所以采用MMX及SSE优化能够大幅度提升程序性能。<br>　　 MMX采用处理器的80位的浮点寄存器的低64位作为MMX寄存器，一共有8个，从mm0到mm7,因为是&#8220;借用&#8221;浮点寄存器的低64位所以每次在用完MMX指令后一定要用EMMS指令将寄存器清空，MMX主要是针对整数运算进行优化，一个64位的MMX寄存器可以同时存入8个8位或者4个16位的整数，估计一次性就可以完成8次8位运算或者4次16位运算，要注意的MMX指令不能直接对32位数进行2次运算，但可以把32位拆分成两个16位再进行运算。MMX技术还有一个非常有用的特性是饱和运算，比如两个8位数相加：128+129相加后很明显超过了8位的最大值256，但是进行饱和运算相加的结果将是最大值256，饱和运算将运算结果控制在相应位数的范围内。<br>　　 <span style="COLOR: red">最近在调试开发一个多媒体相关的程序，发现一个非常奇怪的问题，某些操作之后会导致随后的float数运算结果混乱，花了1个小时的时间才发现是因为自己在使用完MMX指令后忘记用emms指令将浮点寄存器复位！</span></p>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/flyinghare/aggbug/100650.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-11-10 19:11 <a href="http://www.cppblog.com/flyinghare/archive/2009/11/10/100650.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈谈父窗口和所有者窗口</title><link>http://www.cppblog.com/flyinghare/archive/2009/11/04/100098.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 04 Nov 2009 02:44:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/11/04/100098.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/100098.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/11/04/100098.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/100098.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/100098.html</trackback:ping><description><![CDATA[转自：<a href="http://hi.baidu.com/xy2008/blog/item/031e5ab5c8d64acc36d3ca07.html">http://hi.baidu.com/xy2008/blog/item/031e5ab5c8d64acc36d3ca07.html</a><br><br>
<p>一、概念和区别</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 在windows系统中，每个窗口对象都对应有一个数据结构，形成一个list链表。系统的窗口管理器通过这个list来获取窗口信息和管理每个窗口。这个数据结构中有四个数据用来构建list，即child、sibling、parent、owner四个域。<br>&nbsp;&nbsp;&nbsp;&nbsp; 所以我们可以看到，窗口之间的关系有两种：owner-owned 关系和 parent-child关系。前者称之为拥有/被拥有关系，后者称之为父/子关系。在这篇文字中，我把owner窗口称之所有者窗口。换句话说，一个窗口在有一个父窗口（parent)的同时，还可能被不同的窗口拥有（owner)，也可以有自己的子窗口(child)。在MFC 的CWnd类中，所有者窗口保存在m_hWndOwner成员变量中，父窗口则保存在m_hParent中，但是这两个值并不一定和窗口对象数据结构中的值相对应。<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; 窗口之间的关系，决定了窗口的外在表现。比如显示、销毁等。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 如果一个窗口数据的owner域非NULL，则它和该窗口建立了owner-owned 关系，拥有关系决定了：<br>&nbsp;&nbsp;&nbsp;&nbsp; （1）被拥有的窗口永远显示在拥有它的那个窗口的前面；<br>&nbsp;&nbsp;&nbsp;&nbsp; （2）当所有者窗口最小化的时候，它所拥有的窗口都会被隐藏；<br>&nbsp;&nbsp;&nbsp;&nbsp; （3）当所有者窗口被销毁的时候，它所拥有的窗口都会被销毁。<br>&nbsp;&nbsp;&nbsp;&nbsp; 需要注意的是，隐藏所有者窗口并不会影响它所拥有的窗口的可见状态。比如：如果窗口 A 拥有窗口B,窗口B拥有窗口C,则当窗口A最小化的时候，窗口B被隐藏，但是窗口 C还是可见。</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp; 如果一个窗口的parent域非NULL，则它和该窗口之间就建立了parent-child关系。父子决定了：<br>&nbsp;&nbsp;&nbsp;&nbsp; （1）窗口在屏幕上面的显示位置。父窗口提供了用来定位子窗口的坐标系统，一个子窗口只能显示在它的父窗口的客户区中，之外的部分将被裁减。这个裁减法则决定了如果父窗口不可见，则子窗口肯定不可见。如果父窗口移动到了屏幕之外，子窗口也一样。<br>&nbsp;&nbsp;&nbsp;&nbsp; （2）当父窗口被隐藏时，它的所有子窗口也被隐藏。<br>&nbsp;&nbsp;&nbsp;&nbsp; （3）父窗口被销毁的时候，它所拥有的子窗口都会被销毁。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意！最小化父窗口不会影响子窗口的可见状态，子窗口会随着父窗口被最小化，但是它的WS_VISIBLE属性不会变。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Windows系统为什么要使用两种关系呢？这是为了更加灵活的管理窗口。举个例子：组合框（combobox)的下拉列表框（list box）可以超出组合框的父窗口的客户区，这样有利于显示，因此系统创建该list box的时候，是作为控制台窗口（desktop window）的子窗口，它的父窗口hWndParent是NULL，这样，list box的显示区域是限制在整个屏幕内，但是该list box的所有者却是组合框的第一个非子窗口祖先（比如对话框），当它的所有者窗口销毁后，该 list box自动销毁。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 另外，窗口之间消息的传递也和窗口关系有关，通常，一个窗口会把自己的通知消息发送给它的父窗口，但不全是这样，比如，CToolBar发送通知消息给它的所有者窗口而不是父窗口。这样以来，就可以允许工具条作为一个窗口（比如一个 OLE 容器程序窗口）的子窗口的同时，能够给另一个窗口（比如in-place框架窗口）发送消息。至于某类窗口到底是把消息发送给谁，是父窗口还是所有者窗口，microsoft并没有明示。还有，在现场（in-place）编辑的情况下，当一个 server 窗口激活或者失效的时候，框架窗口所拥有的子窗口自动隐藏或者显示，这也是通过直接调用SetOwner函数实现的。</p>
<p>&nbsp;&nbsp;<br>二、窗口类型的说明和限制</p>
<p>（1）控制台窗口（desktop window）。这是系统最早创建的窗口。可以认为它是所有 WS_OVERLAPPED 类型窗口的所有者和父窗口。Kyle Marsh在他的文章&#8220;Win32 Window Hierarchy and Styles&#8221;中指出，当系统初始化的时候，它首先创建控制台窗口，大小覆盖整个屏幕。所有其它窗口都在这个控制台窗口上面显示。窗口管理器所用的窗口list中第一个就是这个控制台。它的下一层窗口叫做顶级窗口（top-level），顶级窗口是指所有非child、没有父窗口，或者父窗口是desktop的窗口，它们没有WS_CHILD属性。</p>
<p>（2）WS_OVERLAPPED类型的窗口可以显示在屏幕的任何地方。它们的所有者窗口是控制台。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Overlapped 类型的窗口属于顶级窗口，一般作为应用程序的主窗口。不论是否给出了WS_CAPTION、WS_BORDER属性，这类窗口创建后都有标题栏和边框。Overlapped窗口可以拥有其它顶级窗口或者被其它顶级窗口所拥有。所有overlapped窗口都有WS_CLIPSIBLINGS属性。系统可以自动设置 overlapped窗口的大小和初始位置。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 当系统 shuts down的时候，它将销毁所有overlapped类型的窗口。</p>
<p>（3）WS_POPUP类型的窗口可以显示在屏幕任何地方，它们一般没有父窗口，但是如果明确调用SetParent，这类窗口也可以有父窗口。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WS_POPUP类型的窗口的所有者是在CreateWindow函数中通过设置hWndParent参数给定的，如果hWndParent不是子窗口，则该窗口就成为这个新的弹出式窗口的owner，否则，系统从hWndParent的父窗口向上找，直到找到第一个非子窗口，把它作为该弹出窗口的owner。当owner窗口销毁的时候，系统自动销毁这个弹出窗口。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Pop-up类型的窗口也属于顶级窗口，它和 overlapped 窗口的主要区别是弹出式窗口不需要有标题栏，也不必有边框。弹出式可以拥有其它顶级窗口或者被拥有。所有弹出式窗口也都有 WS_CLIPSIBLINGS属性。</p>
<p>（4）所有者窗口（owner)只能是 overlapped 或者 pop-up 类型的窗口，子窗口不能是所有者窗口，也就是说子窗口不能拥有其它窗口。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; overlapped 或者 pop-up 类型的窗口在拥有其它窗口的同时，也可以被拥有。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 在使用CreateWindowEx创建 WS_OVERLAPPED 或者 WS_POPUP类型的窗口时，可以在 hwndParent 参数中给出它的所有者窗口的句柄。如果 hwndParent 给出的是一个child 类型的窗口句柄，则系统自动将新创建窗口的所有权交给该子窗口的顶级父窗口。在这种情况下，参数hwndParent被保存在新建窗口的parent域中，而它的所有者窗口句柄则保存在owner域中。</p>
<p>（5）缺省情况下，对话框和消息框属于 owned 窗口，除非在创建它们的时候明确给出了WS_CHILD属性，（比如对话框中嵌入对话框的情形）<br>否则由系统负责给它们指定owner窗口。需要注意的是，一旦创建了owned类型的窗口，就无法再改变其所有关系，因为WIN32没有没有提供改变窗口所有者的方法。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 而且在Win32中，由于有多线程的存在，所以要注意保证父子窗口或者owner/owned 窗口要同属于一个线程。</p>
<p>（6）对于 WS_CHILD类型的窗口，它的父窗口就是它的所有者窗口。一个子窗口的父窗口也是在CreateWindow函数中用hWndParent参数指定的。子窗口只能在父窗口的客户区中显示，并随父窗口一起销毁。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 子窗口必须有一个父窗口，这是它和overlapped 以及 pop-up 窗口之间的主要区别。父窗口可以是顶级窗口，也可以是其它子窗口。</p>
<p><br>三、几个相关函数的说明</p>
<p>（1）获取/设置所有者窗口<br><br>&nbsp;&nbsp;&nbsp;&nbsp; win32 API提供了函数GetWindow函数（GW_OWNER 标志）来获取一个窗口的所有者窗口句柄。<br>&nbsp;&nbsp;&nbsp;&nbsp; GetWindow(hWnd, GW_OWNER)永远返回窗口的所有者(owner)。对于子窗口，函数返回 NULL，因为它们的父窗口就相当于所有者（注意，是&#8220;相当于&#8221;）。因为Windows系统没有维护子窗口的所有者信息。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; MFC中则是通过如下函数得到所有者窗口指针：<br>&nbsp;&nbsp;&nbsp;&nbsp; _AFXWIN_INLINE CWnd* CWnd::GetOwner() const<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { return m_hWndOwner != NULL ? CWnd::FromHandle(m_hWndOwner) : GetParent(); }<br>&nbsp;&nbsp;&nbsp;&nbsp; 从上述代码我们可以看出，它返回的值和GetWindow返回的有所区别，如果当前窗口没有owner，那么将返回它的父窗口指针。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 但是Windows没有提供改变窗口所有者的方法。MFC中则提供了改变所有者的方法：<br>&nbsp;&nbsp;&nbsp;&nbsp; _AFXWIN_INLINE void CWnd::SetOwner(CWnd* pOwnerWnd)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { m_hWndOwner = pOwnerWnd != NULL ? pOwnerWnd-&gt;m_hWnd : NULL; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 另外，mfc还提供了CWnd::GetSafeOwner( CWnd* pParent, HWND* pWndTop );函数，可以用来得到参数pParent的第一个非child属性的父窗口指针。如果这个参数是NULL，则返回当前线程的主窗口(通过AfxGetMainWnd得到)。框架经常使用这个函数查找对话框或者属性页的所有者窗口。</p>
<p>（2）获取/设置父窗口</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WIN32 API给出了函数GetParent和SetParent。而mfc也是完全封装了这两个函数：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; _AFXWIN_INLINE CWnd* CWnd::SetParent(CWnd* pWndNewParent)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::SetParent(m_hWnd,<br>&nbsp;&nbsp;&nbsp; pWndNewParent-&gt;GetSafeHwnd())); }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; _AFXWIN_INLINE CWnd* CWnd::GetParent() const<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::GetParent(m_hWnd)); }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 对于SetParent，msdn里面说明了父子窗口必须是同一个进程的。但是由于窗口句柄是系统全局唯一的，不属于同一个进程的情况下，也可以成功调用，但是后果未知。<br>&nbsp;&nbsp;&nbsp;&nbsp; GetParent的返回值比较复杂，对于overlapped类型的窗口，它返回0，对于WS_CHILD类型，它返回其父窗口，对于WS_POPUP类型，它返回其所有者窗口，如果想得到创建它时所传递进去的那个hwndParent参数，应该用GetWindowWord(GWW_HWNDPARENT)函数。</p>
<p>（3）GetWindowWord(hWnd, GWW_HWNDPARENT)返回一个窗口的父窗口，如果没有，则返回其所有者。</p>
<p>（4）上面谈到，当一个owner窗口被最小化后，系统自动隐藏它所拥有的窗口。当owner窗口被恢复的时候，系统自动显示它所拥有的窗口。在这两种情况下，系统都会发送（send）WM_SHOWWINDOW消息给被拥有的窗口。某些时候，我们可能需要隐藏 owned窗口，但并不想最小化其所有者窗口，这时候，可以通过ShowOwnedPopups函数来实现，该函数设置或者删除当前窗口所拥有的窗口的WS_VISIBLE属性，然后发送WM_SHOWWINDOW消息更新窗口显示。</p>
<img src ="http://www.cppblog.com/flyinghare/aggbug/100098.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-11-04 10:44 <a href="http://www.cppblog.com/flyinghare/archive/2009/11/04/100098.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个简单的完成端口类</title><link>http://www.cppblog.com/flyinghare/archive/2009/10/26/99506.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Mon, 26 Oct 2009 09:56:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/10/26/99506.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/99506.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/10/26/99506.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/99506.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/99506.html</trackback:ping><description><![CDATA[<p>转自：<a href="http://www.vckbase.com/document/viewdoc/?id=1866">http://www.vckbase.com/document/viewdoc/?id=1866</a><br>英文原文：<a href="http://www.codeproject.com/KB/IP/iocp_server_client.aspx">http://www.codeproject.com/KB/IP/iocp_server_client.aspx</a><br></p>
<br><a title=源代码下载 href="http://www.cppblog.com/Files/flyinghare/IOCP-SRC.zip">源代码下载</a><br><br>源码使用了高级的完成端口（IOCP）技术，该技术可以有效地服务于多客户端。本文提出了一些IOCP编程中出现的实际问题的解决方法，并提供了一个简单的echo版本的可以传输文件的客户端/服务器程序。<br><br>程序截图：<br><img height=476 src="http://www.vckbase.com/document/journal/vckbase54/images/iocp1.gif" width=496> <br><br>1.1 环境要求<br>本文读者需要熟悉C++、TCP/IP、Socket编程、MFC，和多线程。<br>源码使用Winsock 2.0和IOCP技术，要求：<br>Windows NT/2000或以上：要求Windows NT3.5或以后版本<br>Windows 95/98/ME：不支持<br>Visual C++.NET，或完整更新过的Visual C++ 6.0<br><br>1.2 摘要<br>当你开发不同类型的软件，你迟早必须处理C/S的开发。对一个程序员来说，写一个通用的C/S编码是一项困难的工作。本文档提供了一份简单但是功能强大的C/S源码，可以扩展到任何类型的C/S应用程序中。这份源码使用了高级的IOCP技术，该技术可以高效的服务于多客户端。IOCP提供了解决&#8220;每个客户端占用一个线程&#8221;的瓶颈问题的办法，只使用几个处理线程，异步输入/输出来发送/接收。IOCP技术被广泛应用在各种类型的高效服务端，例如Apache等。这份源码也提供了一系列的在处理通信和C/S软件中经常使用的功能，如文件接收/传送功能和逻辑线程池管理。本文重点在于出现在IOCP程序API中实用的解决方案，以及关于源码的全面的文档。另外，一份简单的echo版的可处理多连接和文件传输的C/S程序也在这里提供。<br><br>2.1 引言<br>本文提出了一个类，可以用在客户端和服务端。这个类使用IOCP(Input Output Completion Ports)和异步（非阻塞）机制。&#8230;<br>通过这些简单的源码，你可以：<br>&#183; 服务或连接多客户端和服务端<br>&#183; 异步发送或接收文件<br>&#183; 创建并管理一个逻辑工作者线程池，用以处理繁重的客户端/服务器请求或计算<br><br>找到一份全面但简单的解决客户端/服务器通信的源码是件困难的事情。在网络上找到的源码要么太复杂（超过20个类），要命没有提供足够的效率。本源码的设计尽可能简单，并提供了充足的文档。在这篇文章中，我们简洁的呈现出了Winsock API 2.0支持的IOCP技术，说明了在编写过程中出现的棘手问题，并提出了每一个问题的解决方案。<br>
<p>2.2 异步完成端口介绍</p>
<p>如果一个服务器应用程序不能同时支持多个客户端，那是毫无意义的，为此，通常使用异步I/O请求和多线程。根据定义，一个异步I/O请求会立即返回，而留下I/O请求处于等待状态。有时，I/O异步请求的结果必须与主线程同步。这可以通过几种不同方式解决。同步可以通过下面的方式实现：</p>
<p>&gt; 使用事件 &#8211; 当异步请求结束时会马上触发一个信号。这种方式的缺点是线程必须检查并等待事件被触发<br>&gt; 使用GetOverlappedResult函数 &#8211; 这种方式与上一种方式有相同的缺点。<br>&gt; 使用Asynchronous Procedure Calls（或APC） &#8211; 这种方式有几个缺点。首先，APC总是在请求线程的上下文中被请求；第二，为了执行APC，请求线程必须在可变等候状态下挂起。<br>&gt; 使用IOCP &#8211; 这种方式的缺点是必须解决很多实际的棘手的编程问题。编写IOCP可能有点麻烦。<br><br>2.2.1 为什么使用IOCP？<br>通过使用IOCP，我们可以解决&#8220;每个客户端占用一个线程&#8221;的问题。通常普遍认为如果软件不能运行在真正的多处理器机器上，执行能力会严重降低。线程是系统资源，而这些资源既不是无限的，也不是低价的。</p>
<p>IOCP提供了一种方式来使用几个线程&#8220;公平的&#8221;处理多客户端的输入/输出。线程被挂起，不占用CPU周期直到有事可做。<br><br>2.3 什么是IOCP？<br>我们已经看到IOCP只是一个线程同步对象，类似于信号灯，因此IOCP并不是一个复杂的概念。一个IOCP对象与几个支持待定异步I/O请求的I/O对象绑定。一个可以访问IOCP的线程可以被挂起，直到一个待定的异步I/O请求结束。<br><br>3 IOCP是怎样工作的？<br>要使用IOCP，你必须处理三件事情，绑定一个socket到完成端口，创建异步I/O请求，并与线程同步。为从异步I/O请求获得结果，如那个客户端发出的请求，你必须传递两个参数：CompletionKey参数和OVERLAPPED结构。</p>
<p>3.1 关键参数<br>第一个参数：CompletionKey，是一个DWORD类型的变量。你可以传递任何你想传递的唯一值，这个值将总是同该对象绑定。正常情况下会传递一个指向结构或类的指针，该结构或类包含了一些客户端的指定对象。在源码中，传递的是一个指向ClientContext的指针。<br><br>3.2 OVERLAPPED参数</p>
这个参数通常用来传递异步I/O请求使用的内存缓冲。很重要的一点是：该数据将会被锁定并不允许从物理内存中换出页面（page out）。<br><br>3.3 绑定一个socket到完成端口<br>一旦创建完成一个完成端口，可以通过调用CreateIoCompletionPort函数来绑定socket到完成端口。形式如下：
<pre>BOOL IOCPS::AssociateSocketWithCompletionPort(SOCKET socket,  HANDLE hCompletionPort, DWORD dwCompletionKey)
{
HANDLE h = CreateIoCompletionPort((HANDLE) socket,  hCompletionPort, dwCompletionKey, m_nIOWorkers);
return h == hCompletionPort;
}</pre>
3.4 响应异步I/O请求<br>响应具体的异步请求，调用函数WSASend和WSARecv。他们也需要一个参数：WSABUF，这个参数包含了一个指向缓冲的指针。一个重要的规则是：通常当服务器/客户端响应一个I/O操作，不是直接响应，而是提交给完成端口，由I/O工作者线程来执行。这么做的原因是：我们希望公平的分割CPU周期。通过发送状态给完成端口来发出I/O请求，如下：<br>
<pre>BOOL bSuccess = PostQueuedCompletionStatus(m_hCompletionPort,
pOverlapBuff-&gt;GetUsed(),
(DWORD) pContext,
&amp;pOverlapBuff-&gt;m_ol);</pre>
3.5 与线程同步<br><br>与I/O工作者线程同步是通过调用GetQueuedCompletionStatus函数来实现的（如下）。这个函数也提供了CompletionKey参数和OVERLAPPED参数，如下：
<pre>BOOL GetQueuedCompletionStatus(   HANDLE CompletionPort, // handle to completion port
LPDWORD lpNumberOfBytes, // bytes transferred
PULONG_PTR lpCompletionKey, // file completion key
LPOVERLAPPED *lpOverlapped, // buffer
DWORD dwMilliseconds // optional timeout value
);
</pre>
3.6 四个棘手的IOCP编码问题和解决方法<br><br>
<p>使用IOCP时会出现一些问题，其中有一些不是很直观的。在使用IOCP的多线程编程中，一个线程函数的控制流程不是笔直的，因为在线程和通讯直接没有关系。在这一章节中，我们将描述四个不同的问题，可能在使用IOCP开发客户端/服务器应用程序时会出现，分别是：<br></p>
<p>The WSAENOBUFS error problem.（WSAENOBUFS错误问题） <br>The package reordering problem.（包重构问题） <br>The access violation problem.（访问非法问题）</p>
<br><br>3.6.1 WSAENOBUFS问题<br>
<p>这个问题通常很难靠直觉发现，因为当你第一次看见的时候你或许认为是一个内存泄露错误。假定已经开发完成了你的完成端口服务器并且运行的一切良好，但是当你对其进行压力测试的时候突然发现服务器被中止而不处理任何请求了，如果你运气好的话你会很快发现是因为WSAENOBUFS 错误而影响了这一切。 <br><br>每当我们重叠提交一个send或receive操作的时候，其中指定的发送或接收缓冲区就被锁定了。当内存缓冲区被锁定后，将不能从物理内存进行分页。操作系统有一个锁定最大数的限制，一旦超过这个锁定的限制，那么就会产生WSAENOBUFS 错误了。<br><br>如果一个服务器提交了非常多的重叠的receive在每一个连接上，那么限制会随着连接数的增长而变化。如果一个服务器能够预先估计可能会产生的最大并发连接数，服务器可以投递一个使用零缓冲区的receive在每一个连接上。因为当你提交操作没有缓冲区时，那么也不会存在内存被锁定了。使用这种办法后，当你的receive操作事件完成返回时，该socket底层缓冲区的数据会原封不动的还在其中而没有被读取到receive操作的缓冲区来。此时，服务器可以简单的调用非阻塞式的recv将存在socket缓冲区中的数据全部读出来，一直到recv返回 WSAEWOULDBLOCK 为止。 这种设计非常适合那些可以牺牲数据吞吐量而换取巨大 并发连接数的服务器。当然，你也需要意识到如何让客户端的行为尽量避免对服务器造成影响。在上一个例子中，当一个零缓冲区的receive操作被返回后使 用一个非阻塞的recv去读取socket缓冲区中的数据，如果服务器此时可预计到将会有爆发的数据流，那么可以考虑此时投递一个或者多个receive 来取代非阻塞的recv来进行数据接收。（这比你使用1个缺省的8K缓冲区来接收要好的多。） </p>
<p>源码中提供了一个简单实用的解决WSAENOBUF错误的办法。我们执行了一个零字节缓冲的异步WSARead(...)(参见 OnZeroByteRead(..))。当这个请求完成，我们知道在TCP/IP栈中有数据，然后我们通过执行几个有MAXIMUMPACKAGESIZE缓冲的异步WSARead(...)去读，解决了WSAENOBUFS问题。但是这种解决方法降低了服务器的吞吐量。</p>
<p>总结： <br><br>解决方法一： <br><br>投递使用空缓冲区的 receive操作，当操作返回后，使用非阻塞的recv来进行真实数据的读取。因此在完成端口的每一个连接中需要使用一个循环的操作来不断的来提交空缓冲区的receive操作。 <br><br>解决方法二： <br><br>在投递几个普通含有缓冲区的receive操作后，进接着开始循环投递一个空缓冲区的receive操作。这样保证它们按照投递顺序依次返回，这样我们就总能对被锁定的内存进行解锁。 <br></p>
<p>3.6.2 包重构问题<br>... ... 尽管使用IO完成端口的待发操作将总是按照他们发送的顺序来完成，线程调度安排可能使绑定到完成端口的实际工作不按指定的顺序来处理。例如，如果你有两个I/O工作者线程，你可能接收到&#8220;字节块2，字节块1，字节块3&#8221;。这就意味着：当你通过向I/O完成端口提交请求数据发送数据时，数据实际上用重新排序过的顺序发送了。<br><br>这可以通过只使用一个工作者线程来解决，并只提交一个I/O请求，等待它完成。但是如果这么做，我们就失去了IOCP的长处。<br><br>解决这个问题的一个简单实用办法是给我们的缓冲类添加一个顺序数字，如果缓冲顺序数字是正确的，则处理缓冲中的数据。这意味着：有不正确的数字的缓冲将被存下来以后再用，并且因为执行原因，我们保存缓存到一个HASH MAP对象中（如<span name="intelliTxt"><span name="intelliTxt">m_SendBufferMap 和 m_ReadBufferMap</span></span>）。<br><br>获取这种解决方法的更多信息，请查阅源码，仔细查看IOCPS类中如下的函数：</p>
<p>GetNextSendBuffer (..) and GetNextReadBuffer(..), to get the ordered send or receive buffer. <br>IncreaseReadSequenceNumber(..) and IncreaseSendSequenceNumber(..), to increase the sequence numbers. <br><br>3.6.3 异步等待读 和 字节块包处理问题<br><br>最通用的服务端协议是一个基于协议的包，首先X个字节代表包头，包头包含了详细的完整的包的长度。服务端可以读包头，计算出需要多少数据，继续读取直到读完一个完整的包。当服务端同时只处理一个异步请求时工作的很好。但是，如果我们想发挥IOCP服务端的全部潜能，我们应该启用几个等待的异步读事件，等待数据到达。这意味着几个异步读操作是不按顺序完成的，通过等待的读事件返回的字节块流将不会按顺序处理。而且，一个字节块流可以包含一个或几个包，也可能包含部分包，如下图所示：<br><img height=239 src="http://www.vckbase.com/document/journal/vckbase54/images/iocp2.gif" width=291><br><br>这个图形显示了部分包（绿色）和完整包（黄色）是怎样在不同字节块流中异步到达的。<br>这意味着我们必须处理字节流来成功的读取一个完整的包。而且，我们必须处理部分包（图表中绿色的部分）。这就使得字节流的处理更加困难。这个问题的完整解决方法在IOCPS类的ProcessPackage(&#8230;)函数中。</p>
<p>3.6.4 访问非法问题<br>这是一个较小的问题，代码设计导致的问题更胜于IOCP的特定问题。假设一个客户端连接已经关闭并且一个I/O请求返回一个错误标志，然后我们知道客户端已经关闭。在参数CompletionKey中，我们传递了一个指向结构ClientContext的指针，该结构中包含了客户端的特定数据。如果我们释放这个ClientContext结构占用的内存，并且同一个客户端处理的一些其它I/O请求返回了错误代码，我们通过转换参数CompletionKey为一个指向ClientContext结构的指针并试图访问或删除它，会发生什么呢？一个非法访问出现了！</p>
<p>这个问题的解决方法是添加一个数字到结构中，包含等待的I/O请求的数量（m_nNumberOfPendingIO），然后当我们知道没有等待的I/O请求时删除这个结构。这个功能通过函数EnterIoLoop(&#8230;) 和ReleaseClientContext(&#8230;)来实现。</p>
<p>3.7 源码略读<br>源码的目标是提供一系列简单的类来处理所有IOCP编码中的问题。源码也提供了一系列通信和C/S软件中经常使用的函数，如文件接收/传送函数，逻辑线程池处理，等等。<br><img height=495 src="http://www.vckbase.com/document/journal/vckbase54/images/iocp3.gif" width=496><br><br>上图功能性的图解说明了IOCP类源码。<br><br>我们有几个IO工作者线程通过完成端口来处理异步IO请求，这些工作者线程调用一些虚函数，这些虚函数可以把需要大量计算的请求放到一个工作队列中。逻辑工作者通过类中提供的这些函数从队列中取出任务、处理并发回结果。GUI经常与主类通信，通过Windows消息（因为MFC不是线程安全的）、通过调用函数或通过使用共享的变量。<br><br><img height=363 src="http://www.vckbase.com/document/journal/vckbase54/images/iocp4.gif" width=280>图三<br><br>上图显示了类结构纵览。<br><br>图3中的类说明如下：</p>
<p>&gt; CIOCPBuffer：管理异步请求的缓存的类。<br>&gt; IOCPS：处理所有通信的主类。<br>&gt; JobItem：保存逻辑工作者线程要处理的任务的结构。<br>&gt; ClientContex：保存客户端特定信息的结构（如状态、数据，等等）。</p>
<p>3.7.1 缓冲设计 - CIOCPBuffer类<br>使用异步I/O调用时，我们必须提供私有的缓冲区供I/O操作使用。<br>当我们将帐号信息放入分配的缓冲供使用时有许多情况需要考虑：</p>
<p>.分配和释放内存代价高，因此我们应重复使用以及分配的缓冲(内存)，<br>因此我们将缓冲保存在列表结构中，如下所示：</p>
<pre>// Free Buffer List..
CCriticalSection m_FreeBufferListLock;
CPtrList m_FreeBufferList;
// OccupiedBuffer List.. (Buffers that is currently used)
CCriticalSection m_BufferListLock;
CPtrList m_BufferList;
// Now we use the function AllocateBuffer(..)
// to allocate memory or reuse a buffer.</pre>
.有时，当异步I/O调用完成后，缓冲里可能不是完整的包，因此我们需要分割缓冲去取得完整的信息。在CIOCPS类中提供了SplitBuffer函数。<br>同样，有时候我们需要在缓冲间拷贝信息，IOCPS类提供了AddAndFlush函数。<br><br>. 众所周知，我们也需要添加序号和状态（IOType 变量, IOZeroReadCompleted, 等等）到我们的缓冲中。<br><br>. 我们也需要有将数据转换到字节流或将字节流转换到数据的方法，CIOCPBuffer也提供了这些函数。<br><br><br>
<p>以上所有问题都在CIOCPBuffer中解决。</p>
<p>3.8 如何使用源代码<br>从IOCP继承你自己的类（如图3），实现IOCPS类中的虚函数（例如，threadpool），<br>在任何类型的服务端或客户端中实现使用少量的线程有效地管理大量的连接。</p>
<p>3.8.1 启动和关闭服务端/客户端</p>
<br>调用下面的函数启动服务端<br>
<pre>BOOL Start(int nPort=999,int iMaxNumConnections=1201,
int iMaxIOWorkers=1,int nOfWorkers=1,
int iMaxNumberOfFreeBuffer=0,
int iMaxNumberOfFreeContext=0,
BOOL bOrderedSend=TRUE,
BOOL bOrderedRead=TRUE,
int iNumberOfPendlingReads=4);</pre>
<p>nPort<br>服务端侦听的端口. ( -1 客户端模式.)</p>
<p>iMaxNumConnections <br>允许最大的连接数. (使用较大的数.)</p>
<p>iMaxIOWorkers <br>I/O工作线程数</p>
<p>nOfWorkers <br>逻辑工作者数量Number of logical workers. (可以在运行时改变.)</p>
<p>iMaxNumberOfFreeBuffer <br>重复使用的缓冲最大数. (-1 不使用, 0= 不限)</p>
<p>iMaxNumberOfFreeContext <br>重复使用的客户端信息对象数 (-1 for 不使用, 0= 不限)</p>
<p>bOrderedRead <br>顺序读取. (我们已经在 3.6.2. 处讨论过)</p>
<p>bOrderedSend <br>顺序写入. (我们已经在 3.6.2. 处讨论过)</p>
<p>iNumberOfPendlingReads<br>等待读取数据时未决的异步读取循环数<br></p>
<p>连接到远程服务器（客户端模式nPort=-1），调用函数：<br>CodeConnect(const CString &amp;strIPAddr, int nPort)</p>
<p>.strIPAddr <br>远程服务器的IP地址</p>
<p>.nPort <br>端口</p>
<p>调用ShutDown()关闭连接</p>
例如：<br>
<pre>if(!m_iocp.Start(-1,1210,2,1,0,0))
AfxMessageBox("Error could not start the Client");
&#8230;.
m_iocp.ShutDown();
</pre>
<p>4.1 源代码描述<br>更多关于源代码的信息请参考代码里的注释。</p>
<p>4.1.1 虚函数<br>NotifyNewConnection <br>新的连接已接受</p>
<p>NotifyNewClientContext <br>空的ClientContext结构被分配</p>
<p>NotifyDisconnectedClient <br>客户端连接断开</p>
<p>ProcessJob <br>逻辑工作者需要处理一个工作</p>
<p>NotifyReceivedPackage <br>新的包到达</p>
<p>NotifyFileCompleted <br>文件传送完成。</p>
<p>4.1.2 重要变量<br>所有变量共享使用时必须加锁避免存取违例，所有需要加锁的变量，名称为XXX则锁变量名称为XXXLock。</p>
<p>m_ContextMapLock; <br>保存所有客户端数据（socket,客户端数据,等等）</p>
<p>ContextMap m_ContextMap; <br>m_NumberOfActiveConnections <br>保存已连接的连接数</p>
<p>4.1.3 重要函数<br>GetNumberOfConnections() <br>返回连接数</p>
<p>CString GetHostAdress(ClientContext* p) <br>提供客户端上下文，返回主机地址</p>
<p>BOOL ASendToAll(CIOCPBuffer *pBuff); <br>发送缓冲上下文到所有连接的客户端</p>
<p>DisconnectClient(CString sID) <br>根据客户端唯一编号，断开指定的客户端</p>
<p>CString GetHostIP() <br>返回本地IP</p>
<p>JobItem* GetJob() <br>将JobItem从队列中移出, 如果没有job，返回 NULL</p>
<p>BOOL AddJob(JobItem *pJob) <br>添加Job到队列</p>
<p>BOOL SetWorkers(int nThreads) <br>设置可以任何时候调用的逻辑工作者数量</p>
<p>DisconnectAll(); <br>断开所有客户端</p>
<p>ARead(&#8230;) <br>异步读取</p>
<p>ASend(&#8230;) <br>异步发送，发送数据到客户端</p>
<p>ClientContext* FindClient(CString strClient) <br>根据字符串ID寻找客户（非线程安全）</p>
<p>DisconnectClient(ClientContext* pContext, BOOL bGraceful=FALSE); <br>端口客户</p>
<p>DisconnectAll() <br>端口所有客户</p>
<p>StartSendFile(ClientContext *pContext) <br>根据ClientContext结构发送文件（使用经优化的transmitfile(..) 函数）</p>
<p>PrepareReceiveFile(..) <br>接收文件准备。调用该函数时，所有进入的字节流已被写入到文件。</p>
<p>PrepareSendFile(..) <br>打开文件并发送包含文件信息的数据包。函数禁用ASend(..)函数，直到文件传送关闭或中断。</p>
<p>DisableSendFile(..) <br>禁止发送文件模式</p>
<p>DisableRecevideFile(..) <br>禁止文件接收模式<br></p>
<p>5.1 文件传输<br>文件传输使用Winsock 2.0 中的TransmitFile函数。TransmitFile函数通过连接的socket句柄传送文件数据。函数使用操作系统的高速缓冲管理器(cache manager)接收文件数据,通过sockets提供高性能的文件数据传输。异步文件传输要点：<br>在TransmitFile函数返回前，所有其他发送或写入到该socket的操作都将无法执行，因为这将使文件数据混乱。<br>因此，在PrepareSendFile()函数调用之后，所有ASend都被禁止。<br>因为操作系统连续读取文件数据，你可以使用FILE_FLAG_SEQUENTIAL_SCAN参数来优化高速缓冲性能。<br>发送文件时我们使用了内核异步操作(TF_USE_KERNEL_APC)。TF_USE_KERNEL_APC的使用可以更好地提升性能。有可能, 无论如何,TransmitFile在线程中的大量使用，这种情形可能会阻止APCs的调用. </p>
<p>文件传输按如下顺序执行：服务器调用PrepareSendFile(..)函数初始化文件传输。客户端接收文件信息时，调用PrepareReceiveFile(..)作接收前的准备，并发送一个包到服务器告知开始文件传送。当包到达服务器端，服务器端调用StartSendFile(..)采用高性能的TransmitFile函数发送指定文件。<br><br>6 源代码示例<br>提供的源代码演示代码是一个echo客户端/服务器端程序，并提供了对文件传输的支持（图4）。在代码中，MyIOCP类从IOCP继承，处理客户端/服务器端的通讯，所涉及的虚函数可以参见4.1.1处。<br>客户端或服务器端最重要的部分是虚函数NotifyReceivedPackage，定义如下：</p>
<pre>void MyIOCP::NotifyReceivedPackage(CIOCPBuffer *pOverlapBuff,
int nSize,ClientContext *pContext)
{
BYTE PackageType=pOverlapBuff-&gt;GetPackageType();
switch (PackageType)
{
case Job_SendText2Client :
Packagetext(pOverlapBuff,nSize,pContext);
break;
case Job_SendFileInfo :
PackageFileTransfer(pOverlapBuff,nSize,pContext);
break;
case Job_StartFileTransfer:
PackageStartFileTransfer(pOverlapBuff,nSize,pContext);
break;
case Job_AbortFileTransfer:
DisableSendFile(pContext);
break;
};
}
</pre>
<p>函数接收进入的信息并执行远程连接发送的请求。这种情况，只是简单的echo或文件传输的情形。服务器端和客户端源代码分成两个工程，IOCP和IOCPClient。</p>
<p>6.1 编译问题<br>使用VC++6.0或VC.NET编译，你可能在编译CFile时得到一些奇怪的错误，如：</p>
<br>&#8220;if (pContext-&gt;m_File.m_hFile != <br>INVALID_HANDLE_VALUE) &lt;-error C2446: '!=' : no conversion "<br>"from 'void *' to 'unsigned int'&#8221;<br><br>
<p>这个问题可以通过更新头文件(*.h)或VC++ 6.0的版本或改变类型转换错误来解决，在修正了错误后，服务器端/客户端源代码可以在不需MFC的情况下使用。<br></p>
<p>7 特别的考虑和经验总结<br>当你在其他类型的程序中使用本代码，有一些可以避免的编程陷阱和多线程问题。<br>非确定错误指的是那些随机出现的错误，很难通过执行相同顺序的任务来重现这些错误。<br>这是最坏的错误类型，通常，错误出现在内部源代码的设计中。当服务器端有多个IO工作线程在运行，<br>为客户端提供连接，如果程序员没有考虑多线程环境，可能会发生存取违例等不确定错误。</p>
<p>经验 #1:<br>在未对上下文加锁时，不要读写客户上下文（例如：ClientContext）。<br>通知函数（例如：Notify*(ClientContext *pContext)）已经是线程安全，处理成员变量ClientContext可以不需要解锁、解锁。<br></p>
<pre>//不要这样使用
// &#8230;
If(pContext-&gt;m_bSomeData)
pContext-&gt;m_iSomeData=0;
// &#8230;
</pre>
<pre>// 应该这样使用
//&#8230;.
pContext-&gt;m_ContextLock.Lock();
If(pContext-&gt;m_bSomeData)
pContext-&gt;m_iSomeData=0;
pContext-&gt;m_ContextLock.Unlock();
//&#8230;<br></pre>
<p>大家都知道的，当你锁定上下文，其他线程或GUI都将等待它。</p>
<p>经验#2:<br>避免使用复杂的和其他类型的"上下文锁"，应为容易造成死锁（例如：A在等待B，B在等待C，C在等待A，A死锁）</p>
<pre>pContext-&gt; m_ContextLock.Lock();
//&#8230; code code ..
pContext2-&gt; m_ContextLock.Lock();
// code code..
pContext2-&gt; m_ContextLock.Unlock();
// code code..
pContext-&gt; m_ContextLock.Unlock();</pre>
<p>以上代码可能导致死锁</p>
<p>经验 #3:<br>不要在通知函数(例如：Notify*(ClientContext *pContext))以外处理客户上下文，如果你需要这样做，你<br>要放入<br>m_ContextMapLock.Lock(); <br>&#8230; <br>m_ContextMapLock.Unlock();<br>参考如下代码：</p>
<pre>ClientContext* pContext=NULL ;
m_ContextMapLock.Lock();
pContext = FindClient(ClientID);
// safe to access pContext, if it is not NULL
// and are Locked (Rule of thumbs#1:)
//code .. code..
m_ContextMapLock.Unlock();
// Here pContext can suddenly disappear because of disconnect.
// do not access pContext members here.</pre>
8 将来的工作<br>将来，代码将提供以下功能：<br>添加支持AcceptEx(..)接受新连接，处理短连接和DOS攻击。<br>源代码兼容Win32,STL,WTL等环境。
<img src ="http://www.cppblog.com/flyinghare/aggbug/99506.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-10-26 17:56 <a href="http://www.cppblog.com/flyinghare/archive/2009/10/26/99506.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编指令手册</title><link>http://www.cppblog.com/flyinghare/archive/2009/09/30/97655.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 30 Sep 2009 06:34:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/09/30/97655.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/97655.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/09/30/97655.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/97655.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/97655.html</trackback:ping><description><![CDATA[转自：<a href="http://hi.baidu.com/spirith/blog/item/2a47302ae5dc6f3a5343c1fe.html">http://hi.baidu.com/spirith/blog/item/2a47302ae5dc6f3a5343c1fe.html</a><br><br>
<p><span class=tpc_content>一、数据传输指令<br>　　它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据.<br>　　1. 通用数据传送指令.<br>　　　　MOV　　传送字或字节.<br>　　　　MOVSX　先符号扩展,再传送.<br>　　　　MOVZX　先零扩展,再传送.<br>　　　　PUSH　　把字压入堆栈.<br>　　　　POP　　把字弹出堆栈.<br>　　　　PUSHA　把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.<br>　　　　POPA　　把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.<br>　　　　PUSHAD　把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.<br>　　　　POPAD　把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.<br>　　　　BSWAP　交换32位寄存器里字节的顺序<br>　　　　XCHG　　交换字或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数)<br>　　　　CMPXCHG 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )<br>　　　　XADD　　先交换再累加.( 结果在第一个操作数里 )<br>　　　　XLAT　　字节查表转换.<br>　　　　　　　　── BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即<br>　　　　　　　　0-FFH); 返回 AL 为查表结果. ( [BX+AL]-&gt;AL )<br>　　2. 输入输出端口传送指令.<br>　　　　IN　　　I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )<br>　　　　OUT　　I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )<br>　　　　　输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,<br>　　　　　其范围是 0-65535.<br>　　3. 目的地址传送指令.<br>　　　　LEA　　装入有效地址.<br>　　　　　例: LEA DX,string　;把偏移地址存到DX.<br>　　　　LDS　　传送目标指针,把指针内容装入DS.<br>　　　　　例: LDS SI,string　;把段地址:偏移地址存到DS:SI.<br>　　　　LES　　传送目标指针,把指针内容装入ES.<br>　　　　　例: LES DI,string　;把段地址:偏移地址存到ES:DI.<br>　　　　LFS　　传送目标指针,把指针内容装入FS.<br>　　　　　例: LFS DI,string　;把段地址:偏移地址存到FS:DI.<br>　　　　LGS　　传送目标指针,把指针内容装入GS.<br>　　　　　例: LGS DI,string　;把段地址:偏移地址存到GS:DI.<br>　　　　LSS　　传送目标指针,把指针内容装入SS.<br>　　　　　例: LSS DI,string　;把段地址:偏移地址存到SS:DI.<br>　　4. 标志传送指令.<br>　　　　LAHF　　标志寄存器传送,把标志装入AH.<br>　　　　SAHF　　标志寄存器传送,把AH内容装入标志寄存器.<br>　　　　PUSHF　标志入栈.<br>　　　　POPF　　标志出栈.<br>　　　　PUSHD　32位标志入栈.<br>　　　　POPD　　32位标志出栈. <br>二、算术运算指令<br><br>　　　　ADD　　加法.<br>　　　　ADC　　带进位加法.<br>　　　　INC　　加 1.<br>　　　　AAA　　加法的ASCII码调整.<br>　　　　DAA　　加法的十进制调整.<br>　　　　SUB　　减法.<br>　　　　SBB　　带借位减法.<br>　　　　DEC　　减 1.<br>　　　　NEC　　求反(以 0 减之).<br>　　　　CMP　　比较.(两操作数作减法,仅修改标志位,不回送结果).<br>　　　　AAS　　减法的ASCII码调整.<br>　　　　DAS　　减法的十进制调整.<br>　　　　MUL　　无符号乘法.<br>　　　　IMUL　　整数乘法.<br>　　　　　以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),<br>　　　　AAM　　乘法的ASCII码调整.<br>　　　　DIV　　无符号除法.<br>　　　　IDIV　　整数除法.<br>　　　　　以上两条,结果回送:<br>　　　　　　　商回送AL,余数回送AH, (字节运算);<br>　　　　　或　商回送AX,余数回送DX, (字运算).<br>　　　　AAD　　除法的ASCII码调整.<br>　　　　CBW　　字节转换为字. (把AL中字节的符号扩展到AH中去)<br>　　　　CWD　　字转换为双字. (把AX中的字的符号扩展到DX中去)<br>　　　　CWDE　　字转换为双字. (把AX中的字符号扩展到EAX中去)<br>　　　　CDQ　　双字扩展.　　(把EAX中的字的符号扩展到EDX中去) <br><br>三、逻辑运算指令<br><br>　　 　AND　　与运算.<br>　　　　OR　　　或运算.<br>　　　　XOR　　异或运算.<br>　　　　NOT　　取反.<br>　　　　TEST　　测试.(两操作数作与运算,仅修改标志位,不回送结果).<br>　　　　SHL　　逻辑左移.<br>　　　　SAL　　算术左移.(=SHL)<br>　　　　SHR　　逻辑右移.<br>　　　　SAR　　算术右移.(=SHR)<br>　　　　ROL　　循环左移.<br>　　　　ROR　　循环右移.<br>　　　　RCL　　通过进位的循环左移.<br>　　　　RCR　　通过进位的循环右移.<br>　　　　　以上八种移位指令,其移位次数可达255次.<br>　　　　　　　移位一次时, 可直接用操作码.　如 SHL AX,1.<br>　　　　　　　移位&gt;1次时, 则由寄存器CL给出移位次数.<br>　　　　　　　　如　MOV CL,04<br>　　　　　　　　　　SHL AX,CL <br><br>四、串指令<br><br>　DS:SI　源串段寄存器　:源串变址.<br>　　　　　　ES:DI　目标串段寄存器:目标串变址.<br>　　　　　　CX　　　重复次数计数器.<br>　　　　　　AL/AX　扫描值.<br>　　　　　　D标志　0表示重复操作中SI和DI应自动增量; 1表示应自动减量.<br>　　　　　　Z标志　用来控制扫描或比较操作的结束.<br>　　　　MOVS　　串传送.<br>　　　　　　( MOVSB　传送字符.　　MOVSW　传送字.　　MOVSD　传送双字. )<br>　　　　CMPS　　串比较.<br>　　　　　　( CMPSB　比较字符.　　CMPSW　比较字. )<br>　　　　SCAS　　串扫描.<br>　　　　　　把AL或AX的内容与目标串作比较,比较结果反映在标志位.<br>　　　　LODS　　装入串.<br>　　　　　　把源串中的元素(字或字节)逐一装入AL或AX中.<br>　　　　　　( LODSB　传送字符.　　LODSW　传送字.　　LODSD　传送双字. )<br>　　　　STOS　　保存串.<br>　　　　　　是LODS的逆过程.<br>　　　　REP　　　　　　当CX/ECX&lt;&gt;0时重复.<br>　　　　REPE/REPZ　　　当ZF=1或比较结果相等,且CX/ECX&lt;&gt;0时重复.<br>　　　　REPNE/REPNZ　　当ZF=0或比较结果不相等,且CX/ECX&lt;&gt;0时重复.<br>　　　　REPC　　　　　当CF=1且CX/ECX&lt;&gt;0时重复.<br>　　　　REPNC　　　　　当CF=0且CX/ECX&lt;&gt;0时重复. <br><br>五、程序转移指令<br><br>　　1&gt;无条件转移指令 (长转移)<br>　　　　JMP　　无条件转移指令<br>　　　　CALL　　过程调用<br>　　　　RET/RETF过程返回.<br>　　2&gt;条件转移指令 (短转移,-128到+127的距离内)<br>　　　　( 当且仅当(SF XOR OF)=1时,OP1&lt;OP2 )<br>　　　　JA/JNBE 不小于或不等于时转移.<br>　　　　JAE/JNB 大于或等于转移.<br>　　　　JB/JNAE 小于转移.<br>　　　　JBE/JNA 小于或等于转移.<br>　　　　　以上四条,测试无符号整数运算的结果(标志C和Z).<br>　　　　JG/JNLE 大于转移.<br>　　　　JGE/JNL 大于或等于转移.<br>　　　　JL/JNGE 小于转移.<br>　　　　JLE/JNG 小于或等于转移.<br>　　　　　以上四条,测试带符号整数运算的结果(标志S,O和Z).<br>　　　　JE/JZ　等于转移.<br>　　　　JNE/JNZ 不等于时转移.<br>　　　　JC　　　有进位时转移.<br>　　　　JNC　　无进位时转移.<br>　　　　JNO　　不溢出时转移.<br>　　　　JNP/JPO 奇偶性为奇数时转移.<br>　　　　JNS　　符号位为 "0" 时转移.<br>　　　　JO　　　溢出转移.<br>　　　　JP/JPE　奇偶性为偶数时转移.<br>　　　　JS　　　符号位为 "1" 时转移.<br>　　3&gt;循环控制指令(短转移)<br>　　　　LOOP　　　　　　CX不为零时循环.<br>　　　　LOOPE/LOOPZ　　CX不为零且标志Z=1时循环.<br>　　　　LOOPNE/LOOPNZ　CX不为零且标志Z=0时循环.<br>　　　　JCXZ　　　　　　CX为零时转移.<br>　　　　JECXZ　　　　　ECX为零时转移.<br>　　4&gt;中断指令<br>　　　　INT　　中断指令<br>　　　　INTO　　溢出中断<br>　　　　IRET　　中断返回<br>5&gt;处理器控制指令<br>　　　　HLT　　处理器暂停, 直到出现中断或复位信号才继续.<br>　　　　WAIT　　当芯片引线TEST为高电平时使CPU进入等待状态.<br>　　　　ESC　　转换到外处理器.<br>　　　　LOCK　　封锁总线.<br>　　　　NOP　　空操作.<br>　　　　STC　　置进位标志位.<br>　　　　CLC　　清进位标志位.<br>　　　　CMC　　进位标志取反.<br>　　　　STD　　置方向标志位.<br>　　　　CLD　　清方向标志位.<br>　　　　STI　　置中断允许位.<br>　　　　CLI　　清中断允许位. <br>六、伪指令<br>DW　　　定义字(2字节).<br>　　　　PROC　　定义过程.<br>　　　　ENDP　　过程结束.<br>　　　　SEGMENT 定义段.<br>　　　　ASSUME　建立段寄存器寻址.<br>　　　　ENDS　　段结束.<br>　　　　END　　程序结束.</span><br></p>
<img src ="http://www.cppblog.com/flyinghare/aggbug/97655.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-09-30 14:34 <a href="http://www.cppblog.com/flyinghare/archive/2009/09/30/97655.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编指令速查手册</title><link>http://www.cppblog.com/flyinghare/archive/2009/09/11/95905.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Fri, 11 Sep 2009 04:22:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/09/11/95905.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/95905.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/09/11/95905.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/95905.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/95905.html</trackback:ping><description><![CDATA[转自：<a href="http://weibing.blogbus.com/logs/3094931.html">http://weibing.blogbus.com/logs/3094931.html</a><br><br>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不知哪位大哥总结的，先借来用一下，免得老是翻书^_^&nbsp; <br>附一个汇编的在线<a title=asm href="http://bb.jnu.edu.cn/xxkxjsxy/hbyy/IBM-PC/" target=_blank><u>学习网站</u></a></p>
<p><u></u>
<p>数据传输指令<br>───────────────────────────────────────<br>它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据.<br>1. 通用数据传送指令.<br>MOV 传送字或字节.<br>MOVSX 先符号扩展,再传送.<br>MOVZX 先零扩展,再传送.<br>PUSH 把字压入堆栈.<br>POP 把字弹出堆栈.<br>PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.<br>POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.<br>PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.<br>POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.<br>BSWAP 交换32位寄存器里字节的顺序<br>XCHG 交换字或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数)<br>CMPXCHG 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )<br>XADD 先交换再累加.( 结果在第一个操作数里 )<br>XLAT 字节查表转换.<br>── BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即<br>0-FFH); 返回 AL 为查表结果. ( [BX+AL]-&gt;AL )<br>2. 输入输出端口传送指令.<br>IN I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )<br>OUT I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )<br>输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,<br>其范围是 0-65535.<br>3. 目的地址传送指令.<br>LEA 装入有效地址.<br>例: LEA DX,string ;把偏移地址存到DX.<br>LDS 传送目标指针,把指针内容装入DS.<br>例: LDS SI,string ;把段地址:偏移地址存到DS:SI.<br>LES 传送目标指针,把指针内容装入ES.<br>例: LES DI,string ;把段地址:偏移地址存到ES:DI.<br>LFS 传送目标指针,把指针内容装入FS.<br>例: LFS DI,string ;把段地址:偏移地址存到FS:DI.<br>LGS 传送目标指针,把指针内容装入GS.<br>例: LGS DI,string ;把段地址:偏移地址存到GS:DI.<br>LSS 传送目标指针,把指针内容装入SS.<br>例: LSS DI,string ;把段地址:偏移地址存到SS:DI.<br>4. 标志传送指令.<br>LAHF 标志寄存器传送,把标志装入AH.<br>SAHF 标志寄存器传送,把AH内容装入标志寄存器.<br>PUSHF 标志入栈.<br>POPF 标志出栈.<br>PUSHD 32位标志入栈.<br>POPD 32位标志出栈.<br><br>二、算术运算指令<br>───────────────────────────────────────<br>ADD 加法.<br>ADC 带进位加法.<br>INC 加 1.<br>AAA 加法的ASCII码调整.<br>DAA 加法的十进制调整.<br>SUB 减法.<br>SBB 带借位减法.<br>DEC 减 1.<br>NEC 求反(以 0 减之).<br>CMP 比较.(两操作数作减法,仅修改标志位,不回送结果).<br>AAS 减法的ASCII码调整.<br>DAS 减法的十进制调整.<br>MUL 无符号乘法.<br>IMUL 整数乘法.<br>以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),<br>AAM 乘法的ASCII码调整.<br>DIV 无符号除法.<br>IDIV 整数除法.<br>以上两条,结果回送:<br>商回送AL,余数回送AH, (字节运算);<br>或 商回送AX,余数回送DX, (字运算).<br>AAD 除法的ASCII码调整.<br>CBW 字节转换为字. (把AL中字节的符号扩展到AH中去)<br>CWD 字转换为双字. (把AX中的字的符号扩展到DX中去)<br>CWDE 字转换为双字. (把AX中的字符号扩展到EAX中去)<br>CDQ 双字扩展. (把EAX中的字的符号扩展到EDX中去)<br><br>三、逻辑运算指令<br>───────────────────────────────────────<br>AND 与运算.<br>OR 或运算.<br>XOR 异或运算.<br>NOT 取反.<br>TEST 测试.(两操作数作与运算,仅修改标志位,不回送结果).<br>SHL 逻辑左移.<br>SAL 算术左移.(=SHL)<br>SHR 逻辑右移.<br><font color=#ff3300>SAR 算术右移.<strike>(=SHR)</strike></font>&nbsp; 当值为负时，高位补 1 ；当值为正时，高位补 0 <br>ROL 循环左移.<br>ROR 循环右移.<br>RCL 通过进位的循环左移.<br>RCR 通过进位的循环右移.<br>以上八种移位指令,其移位次数可达255次.<br>移位一次时, 可直接用操作码. 如 SHL AX,1.<br>移位&gt;1次时, 则由寄存器CL给出移位次数.<br>如 MOV CL,04<br>SHL AX,CL<br><br>四、串指令<br>───────────────────────────────────────<br>DS:SI 源串段寄存器 :源串变址.<br>ES:DI 目标串段寄存器:目标串变址.<br>CX 重复次数计数器.<br>AL/AX 扫描值.<br>D标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量.<br>Z标志 用来控制扫描或比较操作的结束.<br>MOVS 串传送.<br>( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )<br>CMPS 串比较.<br>( CMPSB 比较字符. CMPSW 比较字. )<br>SCAS 串扫描.<br>把AL或AX的内容与目标串作比较,比较结果反映在标志位.<br>LODS 装入串.<br>把源串中的元素(字或字节)逐一装入AL或AX中.<br>( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. )<br>STOS 保存串.<br>是LODS的逆过程.<br>REP 当CX/ECX&lt;&gt;0时重复.<br>REPE/REPZ 当ZF=1或比较结果相等,且CX/ECX&lt;&gt;0时重复.<br>REPNE/REPNZ 当ZF=0或比较结果不相等,且CX/ECX&lt;&gt;0时重复.<br>REPC 当CF=1且CX/ECX&lt;&gt;0时重复.<br>REPNC 当CF=0且CX/ECX&lt;&gt;0时重复.<br><br>五、程序转移指令<br>───────────────────────────────────────<br>1&gt;无条件转移指令 (长转移)<br>JMP 无条件转移指令<br>CALL 过程调用<br>RET/RETF过程返回.<br>2&gt;条件转移指令 (短转移,-128到+127的距离内)<br>( 当且仅当(SF XOR OF)=1时,OP1 JA/JNBE 不小于或不等于时转移.<br>JAE/JNB 大于或等于转移.<br>JB/JNAE 小于转移.<br>JBE/JNA 小于或等于转移.<br>以上四条,测试无符号整数运算的结果(标志C和Z).<br>JG/JNLE 大于转移.<br>JGE/JNL 大于或等于转移.<br>JL/JNGE 小于转移.<br>JLE/JNG 小于或等于转移.<br>以上四条,测试带符号整数运算的结果(标志S,O和Z).<br>JE/JZ 等于转移.<br>JNE/JNZ 不等于时转移.<br>JC 有进位时转移.<br>JNC 无进位时转移.<br>JNO 不溢出时转移.<br>JNP/JPO 奇偶性为奇数时转移.<br>JNS 符号位为 "0" 时转移.<br>JO 溢出转移.<br>JP/JPE 奇偶性为偶数时转移.<br>JS 符号位为 "1" 时转移.<br>3&gt;循环控制指令(短转移)<br>LOOP CX不为零时循环.<br>LOOPE/LOOPZ CX不为零且标志Z=1时循环.<br>LOOPNE/LOOPNZ CX不为零且标志Z=0时循环.<br>JCXZ CX为零时转移.<br>JECXZ ECX为零时转移.<br>4&gt;中断指令<br>INT 中断指令<br>INTO 溢出中断<br>IRET 中断返回<br>5&gt;处理器控制指令<br>HLT 处理器暂停, 直到出现中断或复位信号才继续.<br>WAIT 当芯片引线TEST为高电平时使CPU进入等待状态.<br>ESC 转换到外处理器.<br>LOCK 封锁总线.<br>NOP 空操作.<br>STC 置进位标志位.<br>CLC 清进位标志位.<br>CMC 进位标志取反.<br>STD 置方向标志位.<br>CLD 清方向标志位.<br>STI 置中断允许位.<br>CLI 清中断允许位.<br><br>六、伪指令<br>─────────────────────────────────────<br>DW 定义字(2字节).<br>PROC 定义过程.<br>ENDP 过程结束.<br>SEGMENT 定义段.<br>ASSUME 建立段寄存器寻址.<br>ENDS 段结束.<br>END 程序结束.</p>
<img src ="http://www.cppblog.com/flyinghare/aggbug/95905.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-09-11 12:22 <a href="http://www.cppblog.com/flyinghare/archive/2009/09/11/95905.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：32位地址的寻址方式</title><link>http://www.cppblog.com/flyinghare/archive/2009/09/09/95699.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 09 Sep 2009 08:07:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/09/09/95699.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/95699.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/09/09/95699.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/95699.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/95699.html</trackback:ping><description><![CDATA[原文：<a href="http://www.makechm.com/chmbook/4/3589-9162-8766-6249-5605.html">http://www.makechm.com/chmbook/4/3589-9162-8766-6249-5605.html</a><br><br>
<p style="TEXT-INDENT: 21.25pt">在32位微机系统中，除了支持前面的七种寻址方式外，又提供了一种更灵活、方便，但也更复杂的内存寻址方式，从而使内存地址的寻址范围得到了进一步扩大。</p>
<p style="TEXT-INDENT: 21.25pt">在用16位寄存器来访问存储单元时，只能使用基地址寄存器(<font color=#ff0000>BX</font>和<font color=#ff0000>BP</font>)和变址寄存器(<font color=#ff0000>SI</font>和<font color=#ff0000>DI</font>)来作为地址偏移量的一部分，但在用32位寄存器寻址时，不存在上述限制，所有32位寄存器(<font color=#ff0000>EAX</font>、<font color=#ff0000>EBX</font>、<font color=#ff0000>ECX</font>、<font color=#ff0000>EDX</font>、<font color=#ff0000>ESI</font>、<font color=#ff0000>EDI</font>、<font color=#ff0000>EBP</font>和<font color=#ff0000>ESP</font>)都可以是地址偏移量的一个组成部分。</p>
<p style="TEXT-INDENT: 21.25pt">当用32位地址偏移量进行寻址时，内存地址的偏移量可分为三部分：一个32位基址寄存器，一个可乘1、2、4或8的32位变址寄存器，一个8位/32位的偏移常量，并且这三部分还可进行任意组合，省去其中之一或之二。</p>
<p style="MARGIN-LEFT: 21.25pt">32位基址寄存器是：EAX、EBX、ECX、EDX、ESI、EDI、EBP和ESP；<br>32位变址寄存器是：EAX、EBX、ECX、EDX、ESI、EDI和EBP(除<font color=#ff0000>ESP</font>之外)。</p>
<table cellSpacing=0 cellPadding=2 width="100%" border=0>
    <tbody>
        <tr>
            <td vAlign=top width="58%">
            <p style="TEXT-INDENT: 21.25pt">下面列举几个32位地址寻址指令：</p>
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td width="57%">
                        <p style="TEXT-INDENT: 22pt">MOV AX, [123456H]</p>
                        </td>
                        <td width="43%">MOV EAX, [EBX]</td>
                    </tr>
                    <tr>
                        <td width="57%">
                        <p style="TEXT-INDENT: 22pt">MOV EBX, [ECX*2]</p>
                        </td>
                        <td width="43%">MOV EBX, [EAX 100H]</td>
                    </tr>
                    <tr>
                        <td width="57%">
                        <p style="TEXT-INDENT: 22pt">MOV EDX, [EAX*4 200H]</p>
                        </td>
                        <td width="43%">MOV EBX, [EAX EDX*2]</td>
                    </tr>
                    <tr>
                        <td width="57%">
                        <p style="TEXT-INDENT: 22pt">MOV EBX, [EAX EDX*2 300H]</p>
                        </td>
                        <td width="43%">MOV AX, [ESP]</td>
                    </tr>
                </tbody>
            </table>
            <p style="TEXT-INDENT: 21.25pt">用32位地址偏移量进行寻址的有效地址计算公式归纳如右式所示。</p>
            <p style="TEXT-INDENT: 21.25pt">由于32位寻址方式能使用所有的通用寄存器，所以，和该有效地址相组合的段寄存器也就有新的规定。具体规定如下：
            <p style="TEXT-INDENT: 21.25pt">1、地址中寄存器的书写顺序决定该寄存器是基址寄存器，还是变址寄存器；</p>
            </td>
            <td width="42%">
            <p align=center><img height=216 alt="" src="http://www.cppblog.com/images/cppblog_com/flyinghare/03-8.gif" width=318 border=0></p>
            </td>
        </tr>
    </tbody>
</table>
<p style="TEXT-INDENT: 35pt">如：[<font color=#ff0000>EBX</font> <font color=#ff0000>EBP</font>]中的<font color=#ff0000>EBX</font>是基址寄存器，<font color=#ff0000>EBP</font>是变址寄存器，而[<font color=#ff0000>EBP</font> <font color=#ff0000>EBX</font>]中的<font color=#ff0000>EBP</font>是基址寄存器，<font color=#ff0000>EBX</font>是变址寄存器；</p>
<p style="TEXT-INDENT: 21.25pt">2、默认段寄存器的选用取决于基址寄存器；</p>
<p style="TEXT-INDENT: 21.25pt">3、基址寄存器是<font color=#ff0000>EBP</font>或<font color=#ff0000>ESP</font>时，默认的段寄存器是<font color=#ff0000>SS</font>，否则，默认的段寄存器是<font color=#ff0000>DS</font>；</p>
<p style="TEXT-INDENT: 21.25pt">4、在指令中，如果使用段前缀的方式，那么，显式段寄存器优先。</p>
<p style="TEXT-INDENT: 21.25pt">下面列举几个32位地址寻址指令及其内存操作数的段寄存器。</p>
<div align=center>
<table cellSpacing=0 cellPadding=2 width="60%" border=0>
    <tbody>
        <tr>
            <td width="53%" bgColor=#ffff00>指令的举例</td>
            <center>
            <td width="47%" bgColor=#ffff00>访问内存单元所用的段寄存器</td>
        </tr>
        <tr>
            <td width="53%" bgColor=#00ffff>MOV&nbsp; AX, [123456H]</td>
            <td width="47%" bgColor=#00ffff>；默认段寄存器DS</td>
        </tr>
        <tr>
            <td width="53%">MOV&nbsp; EAX, [EBX EBP]</td>
            <td width="47%">；默认段寄存器DS</td>
        </tr>
        <tr>
            <td width="53%" bgColor=#00ffff>MOV&nbsp; EBX, [EBP EBX]</td>
            <td width="47%" bgColor=#00ffff>；默认段寄存器SS</td>
        </tr>
        <tr>
            <td width="53%">MOV&nbsp; EBX, [EAX 100H]</td>
            <td width="47%">；默认段寄存器DS</td>
        </tr>
        <tr>
            <td width="53%" bgColor=#00ffff>MOV&nbsp; EDX, ES:[EAX*4 200H]</td>
            <td width="47%" bgColor=#00ffff>；显式段寄存器ES</td>
        </tr>
        <tr>
            <td width="53%">MOV&nbsp; [ESP EDX*2], AX&nbsp;</td>
            <td width="47%">；默认段寄存器SS</td>
        </tr>
        <tr>
            <td width="53%" bgColor=#00ffff>MOV&nbsp; EBX, GS:[EAX EDX*2 300H]</td>
            <td width="47%" bgColor=#00ffff>；显式段寄存器GS</td>
        </tr>
        <tr>
            <td width="53%">MOV&nbsp; AX, [ESP]&nbsp;</td>
            <td width="47%">；默认段寄存器SS</td>
        </tr>
    </tbody>
</table>
</center></div>
<img src ="http://www.cppblog.com/flyinghare/aggbug/95699.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-09-09 16:07 <a href="http://www.cppblog.com/flyinghare/archive/2009/09/09/95699.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>INTEL X86 体系 32 位汇编语言速成</title><link>http://www.cppblog.com/flyinghare/archive/2009/09/09/95698.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Wed, 09 Sep 2009 08:02:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/09/09/95698.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/95698.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/09/09/95698.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/95698.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/95698.html</trackback:ping><description><![CDATA[原文：<a href="http://dev.gameres.com/Program/Other/32CPU.htm">http://dev.gameres.com/Program/Other/32CPU.htm</a><br><br>
<table cellSpacing=0 cellPadding=0 width="95%" border=0>
    <tbody>
        <tr>
            <td width="100%"><font face=Tahoma size=2><br><br>　　上回为大家简单介绍了 Visual C++ Inline Assembly，相信已经有人想实际动手来试试了。然而，要想自由使用 Inline Assembly，你首先必须掌握 INTEL X86 体系的 32 位汇编语言。本文正是为那些已经略有 8086 汇编语言基础却没接触过 X86 体系的 32 位汇编语言的同志们准备的。我们将一起了解和深入 INTEL X86 体系的 32 位汇编语言。<br><br>　　因为我们的目标是&#8220;速成&#8221;，如果你能有点基础的话，那么在此之上展开讨论就能让彼此都感觉轻松很多。假若你以前完全没有学习过汇编语言，那么请务必先去找本 8086 汇编语言的教科书来补习补习之后再来阅读本文。<br><br>　　学习一种的汇编语言，必须了解这种 CPU 的寄存器、寻址方式以及各种指令。我们就先从寄存器开始着手吧。<br><br>g<br>INTEL X86 常用寄存器<br><br>通用寄存器 段寄存器<br><br>AH/AL AX (EAX) 累加器 CS 代码段<br>BH/BL BX (EBX) 基址 DS 数据段<br>CH/CL CX (ECX) 计数器 SS 堆栈段<br>DH/DL DX (EDX) 数据 ES 附加段<br>(FS) 386 新增的段寄存器<br>(Exx) 为 386 新增的 32 位寄存器 (GS) 386 新增的段寄存器<br><br><br>指针寄存器 堆栈寄存器<br><br>SI (ESI) 源索引指针 SP (ESP) 栈指针<br>DI (EDI) 目的索引指针 BP (EBP) 基址指针<br>IP 指令指针<br><br><br></font><font face=Fixedsys size=2>状态寄存器<br><br>|11|10|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|<br>| | | | | | | | | | | | | | | | | +--- CF Carry Flag<br>| | | | | | | | | | | | | | | | +--- 1<br>| | | | | | | | | | | | | | | +--- PF Parity Flag<br>| | | | | | | | | | | | | | +--- 0<br>| | | | | | | | | | | | | +--- AF Auxiliary Flag<br>| | | | | | | | | | | | +--- 0<br>| | | | | | | | | | | +--- ZF Zero Flag<br>| | | | | | | | | | +--- SF Sign Flag<br>| | | | | | | | | +--- TF Trap Flag (Single Step)<br>| | | | | | | | +--- IF Interrupt Flag<br>| | | | | | | +--- DF Direction Flag<br>| | | | | | +--- OF Overflow flag<br>| | | | +----- IOPL I/O Privilege Level (286+ only)<br>| | | +----- NT Nested Task Flag (286+ only)<br>| | +----- 0<br>| +----- RF Resume Flag (386+ only)<br>+------ VM Virtual Mode Flag (386+ only)</font><font face=Tahoma size=2><br><br>　　怎么样，看起来大半部分都应该是我们以前很熟的了吧。现在，我们只需要侃侃那些在 386 上才<br>开始出现的新的寄存器就行了。<br><br>　　首先必须强调的是，在用 32 位汇编语言编程的时候，所有的地址偏移量都是 32 位的，在寻址时<br>千万不要还用原来的 16 位方式。<br><br>　　对于通用寄存器来说，多了种形如 (Exx) 的 32 位寄存器，它的低 16 位内容就是原来的 16 位寄<br>存器，而多出的高 16 位的内容，则只能通过使用 32 位寄存器来访问。<br><br>　　再以指针寄存器为例，在寻址时一定要用 ESI、EDI、EBP 等等，必须要把以前那种 mov ax,[si] 之<br>类的指令改为 mov ax,[ESI]。<br><br>　　从 386 开始，多出了 FS、GS 这两个新的段寄存器。由于我们学习的目的是为了今后写在线汇编，所以很多繁琐的问题都不会直接遇到。为了能更快地投入实际运用，这里就不打算去讲述保护模式的细节了，而直接给大家提出一个结论，你只管按照这个结论去做就行了。<br><br>　　这个简单的结论就是：你在 VC 中写在线汇编时，尽量不要去碰段寄存器！<br><br>　　得出这个结论的第一个原因是 VC 生成的应用程序的 DS、ES 和 SS 是相同的。换种说法，整个应用程序的数据段、附加段、堆栈段都在同一个地址，你根本就没有去改变它们的必要。第二个原因是，因为是保护模式，每个段都有 4GB 大小，所有数据都可以轻易地放进去，于是当然就用不着去改段寄存器了。最后一个原因是，保护模式下的段寄存器使用方法同实模式下完全不一样，在你没弄懂以前，最好别去乱改，否则&#8230;&#8230;嘿嘿，死掉了别怨我。<br><br>　　至于状态寄存器，大家肯定是非常熟悉了，虽然多了几位，但这些内容我们一般都用不着，所以也可以略过。<br><br>　　下面将开始讲述 INTEL X86 的 32 位偏移地址构成方式。<br><br>　　这里给出关于 80386 寻址模式的一张列表：<br><br><br></font><font face=Fixedsys size=2>基地址 + (变址 X 比例因子) + 偏移量<br><br>|无 | |无 |<br>|EAX| |EAX|<br>|EBX| |EBX| |1|<br>|ECX| |ECX| |2| | 无 |<br>|EDX| + |EDX| X |4| + | 8位|<br>|ESP| |---| |8| |32位|<br>|EBP| |EBP|<br>|ESI| |ESI|<br>|EDI| |EDI|<br></font><font face=Tahoma size=2><br>其中，&#8220;---&#8221;表示 ESP 不能被用作变址寄存器<br><br>　　注意到比例因子，这个可是从 386 才开始加入的好东西，它对处理表结构大有好处，而且还衍生出了不少技巧，详情请参考《图形程序开发人员指南 (Michael Abrash's Graphics Programming Black Book&nbsp;<br>Special Edition)》一书第 6.3 节。<br><br>　　在 80386 寻址时，其默认的段寄存器取决于所选择的基地址寄存器。如果基地址寄存器是 ESP 或<br>者 EBP，则默认的段寄存器是 SS。对于别的基地址寄存器的选择，包括无基地址寄存器，DS 仍然是其默认的段寄存器。其实，前面已经说了，你在 VC 中写在线汇编时，可以不去碰段寄存器，但反正已经讲到这里了，所以随便就提两句。<br><br>　　其对应关系见下表：<br><br><br>基地址寄存器 默认的段寄存器<br><br></font><font face=Fixedsys size=2>BP or SP SS<br>SI or DI DS<br>DI strings ES<br>SI strings DS</font><font face=Tahoma size=2> <br><br>　　了解了寄存器和寻址方式以后，我们就可以使用过去已经知道的指令开始编程了。但 INTEL 在 386 以后新增了不少指令，虽然这里我不可能全部列出来，但可以稍微看看其中比较常用的几条。<br><br><br></font><font face=Fixedsys size=2>BSWAP - 字节交换 (486+)<br>颠倒 32 位寄存器的字节排列顺序。<br>CDQ - 双字转换成四字 (386+)<br>把 EAX 中的符号数按符号扩展为 EDX:EAX，即把 EAX 中的第 32 位赋予 EDX 的每一位。<br>CWDE - 将字扩展转换成双字 (386+)<br>把 AX 中的符号数按符号扩展为 EAX，即把 AX 中的第 16 位赋予 EAX 的高 16 位。<br>MOVSX - 符号扩展传送 (386+)<br>传送数据时先作符号扩展。<br>MOVZX - 零扩展传送 (386+)<br>传送数据时先作零扩展。<br>SHLD/SHRD - 双精度移位 (386+)<br>把源操作数中的若干位移入目的操作数，源操作数中的内容保持不变。<br>POPA/POPAD - 所有通用寄存器出栈 (80188+)<br>PUSHA/PUSHAD - 所有通用寄存器入栈 (80188+)</font><font face=Tahoma size=2> <br><br>　　怎么样？感觉这些指令挺爽的吧，其实还有很多的好东西（比如各种位处理指令等等）没能在这里提到。更完整和详细的资料请查阅『INTEL 汇编指令集』。<br><br>　　到此为止，该了解的基础知识我们差不多都已经全部提到了，很简单不是？剩下的就是该你自己去多多编程实践了。只有这样，你才能获取宝贵的经验，从而真正地掌握这门汇编语言。<br></font></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/flyinghare/aggbug/95698.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-09-09 16:02 <a href="http://www.cppblog.com/flyinghare/archive/2009/09/09/95698.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：基于MMX指令集的程序设计简介</title><link>http://www.cppblog.com/flyinghare/archive/2009/08/31/94886.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Mon, 31 Aug 2009 07:52:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/08/31/94886.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/94886.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/08/31/94886.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/94886.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/94886.html</trackback:ping><description><![CDATA[<p>英文原文：<a href="http://www.codeproject.com/KB/recipes/mmxintro.aspx">http://www.codeproject.com/KB/recipes/mmxintro.aspx</a><br>翻译原文：<a href="http://www.itepub.net/html/article/bianchengkaifa/cnetok/xitongbiancheng/2006/0428/2101.html">http://www.itepub.net/html/article/bianchengkaifa/cnetok/xitongbiancheng/2006/0428/2101.html</a><br><br>MMX技术简介<br><br>Intel 公司的MMX&#8482;（多媒体增强指令集）技术可以大大提高应用程序对二维三维图形和图象的处理能力。Intel MMX技术可用于对大量数据和复杂数组进行的复杂处理，使用MMX技术可处理的数据基本单位可以是字节（byte）、字（word），或者是双字（double-word）。<br><br>Visual Studio .NET 2003提供了对MMX指令集特性的支持，从而可以不必编写汇编代码，直接使用C++代码就可以实现MMX指令的功能。通过参考Intel软件说明书（Intel Software manuals）[1]以及阅读MSDN中有关MMX编程技术的主题会使你更好地把握MMX编程的要点。<br><br>MMX技术实现了单道指令多道数据流（SIMD，single-instruction, multiple-data）的执行模式。考虑下面一个需要编程完成的任务，在一个字节（BYTE）数组中使其中每一个元素加上一个数，在传统的程序中，实现这个功能的算法如下：<br><br>for each&nbsp;&nbsp;b in array&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//对数组中的每一个元素b<br>&nbsp;&nbsp;&nbsp;&nbsp;b = b + n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//加上一个数n<br><br>下面看看它的实现细节：<br><br>for each&nbsp;&nbsp;b in array&nbsp;&nbsp;//对数组中的每一个元素b<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;把b加载到寄存器中<br>&nbsp;&nbsp;&nbsp;&nbsp;把此寄存器中的数加上n<br>&nbsp;&nbsp;&nbsp;&nbsp;把所得寄存器中的结果放回内存<br>}<br><br><br>具有MMX指令集支持的处理器有八个64位的寄存器，每一个寄存器可以存放8个字节（byte）、4个字(word)或2个双字(double-word)。MMX技术同时提供了一个MMX指令集，其中的指令可以可以把一个数值（其类型可以是字节、字或双字）加载到这些MMX寄存器中，在寄存器中进行算术或逻辑运算，然后把寄存器中的结果放回内存存储单元。上面的例子采用MMX技术后的算法是这样的：<br><br>for each&nbsp;&nbsp;8 members in array&nbsp;&nbsp;//把数组中的8个字节（其中一个字节为数组中的一个单位）作为一组取出<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;把这8个字节加载到MMX寄存器中<br>&nbsp;&nbsp;&nbsp;&nbsp;通过一个CPU指令执行周期把这个寄存器中的8个字节都加上n<br>&nbsp;&nbsp;&nbsp;&nbsp;把寄存器中计算的结果写回内存<br>}<br><br><br>C++编程人员不必直接使用MMX指令集中的指令访问这些MMX寄存器。你可以使用64位的数据类型__m64和一系列C++函数来进行相关的算术和逻辑运算。而决定程序使用哪个MMX寄存器以及代码优化是C++编译器的任务。 <br><br>Visual C++ MMXSwarm [4]是MSDN中提供的一个很好的使用MMX技术进行图象处理的例子，它包含了一些封装好了的类简化了使用MMX技术的操作，并向你展示了对各种不同格式图象进行处理的操作（如单色24位象素RGB、32位象素RGB等）。本文只是对使用Visual C++实现MMX程序设计的简单介绍。如果你感兴趣的话，可以参看MSDN上MMXSwarm的例子。<br><br>MMX程序设计详细介绍<br><br>包含的头文件<br><br>所有的MMX指令集函数在emmintrin.h文件中定义：<br>#include &lt;emmintrin.h&gt;<br>因为程序中用到的MMX处理器指令是由编译器决定，所以它并没有相关的.lib库文件。<br><br>__m64 数据类型 <br><br>这种类型的变量可用作MMX指令的操作数，它不能被直接访问。_m64类型的变量被自动分配为8个字节的字长。<br><br>CPU对MMX指令集的支持<br><br>如果你的CPU能够具有了MMX指令集，你就可以使用Visual Studio .NET 2003提供的对MMX指令集支持的C++函数库了，你可以查看MSDN中的一个Visual C++ CPUID[3]的例子，它可以帮你检测你的CPU是否支持SSE、MMX指令集或其它的CPU功能。<br><br>饱和算法（Saturation Arithmetic）和封装模式（Wraparound Mode）<br><br>MMX技术支持一种叫做saturating arithmetic（饱和算法）的计算模式。在饱和模式下，当计算结果发生溢出（上溢或下溢）时，CPU会自动去掉溢出的部分，使计算结果取该数据类型表示数值的上限值（如果上溢）或下限值（如果下溢）。饱和模式的计算用于对图象的处理。<br>下面的例子能够让你理解饱和模式和封装模式的区别。如果一个字节(BYTE)类型变量的值为255，然后将其值加一。在封装模式下，相加结果为0（去掉进位）；在饱和模式下，结果为255。饱和模式用类似的方法来处理下溢出，比如对于一个字节数据类型的数在饱和模式下，1减2的结果为0（而不是-1）。每一个MMX算术指令都有这两种模式：饱和模式和封装模式。本文所要讨论的项目只使用饱和模式下的MMX指令。<br><br>编程实例<br><br>以下讲解了MMX技术在Visual Studio .NET 2003下的应用实例，你可以在http://www.codeproject.com/cpp/mmxintro/MMX_src.zip下载示例程序压缩包。该压缩包中含有两个项目，这两个项目是基于微软基本类库（MFC）建立的Visual C++.NET项目，你也可以按照下面的讲解建立这两个项目。<br><br>MMX8 演示项目<br><br>MMX8是一个单文档界面（SDI）的应用程序，用来对每象素8位的单色位图进行简单处理。源图象和处理后的图象会在窗体中显示出来。新建的ATL（活动模版库）类 Cimage用来从资源中提取图象并在窗体中显示出来。程序要对图象进行两种处理操作：图象颜色反相和改变图象的亮度。每一种处理操作可以用下面几种方法之中其中的一种来实现：<br><br>纯C++代码；<br>使用C++的MMX功能函数的代码；<br>使用MMX汇编指令的代码。<br><br>对图象进行处理计算的时间会显示在状态栏中。<br><br>用纯C++实现的图象颜色反相函数：<br><br>void CImg8Operations::InvertImageCPlusPlus(<br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pSource, <br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pDest, <br>&nbsp;&nbsp;&nbsp;&nbsp;int nNumberOfPixels)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;for ( int i = 0; i &lt; nNumberOfPixels; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pDest++ = 255 - *pSource++;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><br><br>为了查询使用C++ MMX指令函数的方法，需要参考Intel软件说明书（Intel Software manuals）中有关MMX汇编指令的说明，首先我是在第一卷的第八章找到了MMX相关指令的大体介绍，然后在第二卷找到了有关这些MMX指令的详细说明，这些说明有一部分涉及了与其特性相关的C++函数。然后我通过这些MMX指令对应的C++函数查找了MSDN中与其相关的说明。在MMX8示例程序中用到的MMX指令和相关的C++函数见下表：<br><br><br><br><br>实现的功能 对应的MMX汇编指令 Visual C++.NET中的MMX函数 <br>清除MMX寄存器中的内容，即初始化（以避免和浮点数操作发生冲突）。 emms _mm_empty <br>将两个64位数中对应的（8个）无符号（8位）字节同时进行减法操作。 psubusb _mm_subs_pu8 <br>将两个64位数中对应的（8个）无符号（8位）字节同时进行加法操作。 paddusb _mm_adds_pu8 <br><br>用Visual C++.NET的MMX指令函数实现图象颜色反相的函数：<br><br>void CImg8Operations::InvertImageC_MMX(<br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pSource, <br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pDest, <br>&nbsp;&nbsp;&nbsp;&nbsp;int nNumberOfPixels)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;__int64 i = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;i = ~i;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 0xffffffffffffffff&nbsp;&nbsp;&nbsp;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;// 每次循环处理8个象素<br>&nbsp;&nbsp;&nbsp;&nbsp;int nLoop = nNumberOfPixels/8;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__m64* pIn = (__m64*) pSource;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 输入的字节数组指针<br>&nbsp;&nbsp;&nbsp;&nbsp;__m64* pOut = (__m64*) pDest;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 输出的字节数组指针<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__m64 tmp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 临时工作变量<br><br>&nbsp;&nbsp;&nbsp;&nbsp;_mm_empty();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 执行MMX指令：emms，初始化MMX寄存器<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__m64 n1 = Get_m64(i);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;for ( int i = 0; i &lt; nLoop; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp = _mm_subs_pu8 (n1 , *pIn);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 饱和模式下的无符号减法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//对每一个字节执行操作：tmp = n1 - *pIn <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pOut = tmp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pIn++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 取下面的8个象素点<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pOut++;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;_mm_empty();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 执行MMX指令：emms，清除MMX寄存器中的内容<br>}<br><br>__m64 CImg8Operations::Get_m64(__int64 n)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;union __m64__m64<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__m64 m;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__int64 i;<br>&nbsp;&nbsp;&nbsp;&nbsp;} mi;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;mi.i = n;<br>&nbsp;&nbsp;&nbsp;&nbsp;return mi.m;<br>}<br><br>虽然这个函数在非常短的时间就执行完成了，但我记录了这3种方法需要的时间，以下是在我的计算机上运行的结果：<br><br>纯C++代码&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;43毫秒<br>使用C++的MMX指令函数的代码 26毫秒<br>使用MMX汇编指令的代码&nbsp;&nbsp;&nbsp;26毫秒<br><br>上面的图象处理时间必须在程序Release优化编译后执行时才能体现出很好的效果。<br><br>而改变图象的亮度我采用了最简单的方法：对图象中的每一个象素的颜色值进行加减运算。相对前面的处理函数而言，这样的转换函数有些复杂，因为我们需要把处理过程分成两种情况，一种是增加象素颜色值，另一种是减少象素颜色值。<br><br><br>用纯C++函数实现的改变图象亮度的函数：<br><br>void CImg8Operations::ChangeBrightnessCPlusPlus(<br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pSource, <br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pDest, <br>&nbsp;&nbsp;&nbsp;&nbsp;int nNumberOfPixels, <br>&nbsp;&nbsp;&nbsp;&nbsp;int nChange)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;if ( nChange &gt; 255 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nChange = 255;<br>&nbsp;&nbsp;&nbsp;&nbsp;else if ( nChange &lt; -255 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nChange = -255;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE b = (BYTE) abs(nChange);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;int i, n;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;if ( nChange &gt; 0 ) //增加象素颜色值<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for ( i = 0; i &lt; nNumberOfPixels; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;n = (int)(*pSource++ + b);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( n &gt; 255 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;n = 255;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pDest++ = (BYTE) n;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;&nbsp;&nbsp;&nbsp;//减少象素颜色值<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for ( i = 0; i &lt; nNumberOfPixels; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;n = (int)(*pSource++ - b);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( n &lt; 0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;n = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pDest++ = (BYTE) n;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><br><br>用Visual C++.NET的MMX指令函数实现的改变图象亮度函数：<br><br>void CImg8Operations::ChangeBrightnessC_MMX(<br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pSource, <br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pDest, <br>&nbsp;&nbsp;&nbsp;&nbsp;int nNumberOfPixels, <br>&nbsp;&nbsp;&nbsp;&nbsp;int nChange)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;if ( nChange &gt; 255 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nChange = 255;<br>&nbsp;&nbsp;&nbsp;&nbsp;else if ( nChange &lt; -255 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nChange = -255;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE b = (BYTE) abs(nChange);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__int64 c = b;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;for ( int i = 1; i &lt;= 7; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c = c &lt;&lt; 8;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c |= b;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;// 在一次循环中处理8个象素<br>&nbsp;&nbsp;&nbsp;&nbsp;int nNumberOfLoops = nNumberOfPixels / 8;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__m64* pIn = (__m64*) pSource;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 输入的字节数组<br>&nbsp;&nbsp;&nbsp;&nbsp;__m64* pOut = (__m64*) pDest;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 输出的字节数组<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__m64 tmp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 临时工作变量<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;_mm_empty();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 执行MMX指令：emms<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__m64 nChange64 = Get_m64(c);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;if ( nChange &gt; 0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for ( i = 0; i &lt; nNumberOfLoops; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp = _mm_adds_pu8(*pIn, nChange64); //&nbsp;&nbsp;饱和模式下的无符号加法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 对每一个字节执行操作：tmp = *pIn + nChange64 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pOut = tmp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pIn++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 取下面8个象素<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pOut++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for ( i = 0; i &lt; nNumberOfLoops; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp = _mm_subs_pu8(*pIn, nChange64); //&nbsp;&nbsp;饱和模式下的无符号减法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 对每一个字节执行操作：tmp = *pIn - nChange64<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pOut = tmp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pIn++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//取下面8个象素<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pOut++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;_mm_empty();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 执行MMX指令：emms<br>}<br><br><br>注意参数nChange的符号每次调用函数时在循环体外只检查一次，而不是放在循环体内，那样会被检查成千上万次。下面是在我的计算机上处理图象花费的时间：<br><br>纯C++代码&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;49毫秒<br>使用C++的MMX指令函数的代码 26毫秒<br>使用MMX汇编指令的代码&nbsp;&nbsp;&nbsp;26毫秒<br><br><br>MMX32 演示项目<br><br>MMX32项目可对32位象素的RGB图象进行处理。进行的图象处理工作是图象颜色反相操作和更改图象颜色的平衡度（将象素点的每一种颜色乘以一定的值）操作。<br><br>MMX的乘法实现起来比加减法复杂得多，因为乘法运算通常得出的结果的位数不再是以前位数的大小。比如，如果乘法的操作数有一个字节（8位的BYTE）大小，那么结果会达到一个字（16位的WORD）大小。这需要额外的转换，并且使用MMX汇编指令和C++代码进行图象转换花费时间的差别不是很大（时间差为5-10%）。<br><br>用Visual C++.NET的MMX指令函数实现的更改图象颜色平衡度的函数：<br><br>void CImg32Operations::ColorsC_MMX(<br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pSource, <br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE* pDest, <br>&nbsp;&nbsp;&nbsp;&nbsp;int nNumberOfPixels, <br>&nbsp;&nbsp;&nbsp;&nbsp;float fRedCoefficient, <br>&nbsp;&nbsp;&nbsp;&nbsp;float fGreenCoefficient, <br>&nbsp;&nbsp;&nbsp;&nbsp;float fBlueCoefficient)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;int nRed = (int)(fRedCoefficient * 256.0f);<br>&nbsp;&nbsp;&nbsp;&nbsp;int nGreen = (int)(fGreenCoefficient * 256.0f);<br>&nbsp;&nbsp;&nbsp;&nbsp;int nBlue = (int)(fBlueCoefficient * 256.0f);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;// 设置相乘系数<br>&nbsp;&nbsp;&nbsp;&nbsp;__int64 c = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;c = nRed;<br>&nbsp;&nbsp;&nbsp;&nbsp;c = c &lt;&lt; 16;<br>&nbsp;&nbsp;&nbsp;&nbsp;c |= nGreen;<br>&nbsp;&nbsp;&nbsp;&nbsp;c = c &lt;&lt; 16;<br>&nbsp;&nbsp;&nbsp;&nbsp;c |= nBlue;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__m64 nNull = _m_from_int(0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// null<br>&nbsp;&nbsp;&nbsp;&nbsp;__m64 tmp = _m_from_int(0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 临时工作临时变量初始化<br><br>&nbsp;&nbsp;&nbsp;&nbsp;_mm_empty();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 清空MMX寄存器。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__m64 nCoeff = Get_m64(c);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;DWORD* pIn = (DWORD*) pSource;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 输入双字数组<br>&nbsp;&nbsp;&nbsp;&nbsp;DWORD* pOut = (DWORD*) pDest;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 输出双字数组<br><br>&nbsp;&nbsp;&nbsp;&nbsp;for ( int i = 0; i &lt; nNumberOfPixels; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp = _m_from_int(*pIn);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// tmp = *pIn (在tmp的低32位写入数据)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp = _mm_unpacklo_pi8(tmp, nNull );&nbsp;&nbsp;&nbsp;&nbsp;//将tmp中低位的4个字节转化为字<br>//字的高位用nNull中对应位上的位值填充。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp =&nbsp;&nbsp;_mm_mullo_pi16 (tmp , nCoeff);&nbsp;&nbsp;&nbsp;//将tmp中的每一个字相乘，将相乘结果的高位送到nCoeff，在tmp中只保留每个结果的低位。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp = _mm_srli_pi16 (tmp , 8);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 将tmp中的每一个字右移8位，相当于除以256<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp = _mm_packs_pu16 (tmp, nNull);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 使用饱和模式将tmp中的结果做如下处理：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//将tmp中的4个字转化为4个字节，并将这4个字节写到tmp中的低32位中 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 同时，将nNull中的4个字转化为4个字节，并将这4个字节写到tmp的高32位中。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pOut = _m_to_int(tmp);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// *pOut = tmp (将tmp低32位的数据放入pOut数组中)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pIn++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pOut++;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;_mm_empty();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>}<br><br>你可以参看示例项目的源代码了解有关此项目的更多的细节。<br><br>SSE2 技术<br><br>SSE2技术包含有一个类似MMX中对整数操作的指令集，同时也包含128位的SSE寄存器组。比如，用SSE2技术实现更改图象颜色平衡度能够比用纯C++代码实现此功能在效率上有很大提升。SSE2同时是SSE技术的扩展，比如它不仅可以单精度浮点数数组，而且能够处理双精度浮点数数据类型的数组。用C++实现的MMXSwarm 示例项目不仅使用了MMX指令函数，而且使用了SSE2指令对整型数操作的函数。<br></p>
<img src ="http://www.cppblog.com/flyinghare/aggbug/94886.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-08-31 15:52 <a href="http://www.cppblog.com/flyinghare/archive/2009/08/31/94886.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数调用的汇编码分析</title><link>http://www.cppblog.com/flyinghare/archive/2009/08/14/93315.html</link><dc:creator>会飞的兔子</dc:creator><author>会飞的兔子</author><pubDate>Fri, 14 Aug 2009 07:06:00 GMT</pubDate><guid>http://www.cppblog.com/flyinghare/archive/2009/08/14/93315.html</guid><wfw:comment>http://www.cppblog.com/flyinghare/comments/93315.html</wfw:comment><comments>http://www.cppblog.com/flyinghare/archive/2009/08/14/93315.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyinghare/comments/commentRss/93315.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyinghare/services/trackbacks/93315.html</trackback:ping><description><![CDATA[<p>这几天学习汇编，分析了一下 c++ 中函数调用（cdecl 和 fastcall 方式）过程的汇编码，记录如下：</p>
<p>&nbsp;</p>
<h2>程序例子</h2>
<div>
<p>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>struct tagTest&nbsp; </font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>{</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;int n1;</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;long n2;</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;DWORD n3;</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>};</font></div>
<div></div>
</font>
<p><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;</font></p>
<p><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>long funtest1(tagTest p1,int p2,LPCTSTR lpszP3)&nbsp; </font></p>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>{&nbsp; // 普通函数</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;p1.n1 = 3;</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;LPCTSTR lpszxx = lpszP3;</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;p1.n3 = p2;</font></div>
<div></div>
</font>
<p><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;return 300; </font></p>
<font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>
<div>}</div>
</font>
<div></div>
</font>
<p><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;</font></p>
<p><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>long __stdcall funtest2(tagTest p1,int p2,LPCTSTR lpszP3) </font></p>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>{ // stdcall 函数</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;p1.n1 = 3;</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;LPCTSTR lpszxx = lpszP3;</font></div>
<div><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;p1.n3 = p2;</font></div>
<div></div>
</font>
<p><font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>&nbsp;return 300; </font></p>
<font face="SimSun,宋体,MS Song,serif" color=#0000ff size=3>
<div>}</div>
</font>
<div></div>
</font></div>
<h2>1、普通调用（cdecl）。</h2>
<h3 dir=ltr style="MARGIN-RIGHT: 0px">&nbsp;&nbsp;&nbsp; 调用方 C++ 代码：</h3>
<div>&nbsp;long ixx = 0; </div>
<div>&nbsp;tagTest tag1={34,6,87};</div>
<div>&nbsp;ixx = funtest1(tag1,i2,"asdffffffdddddd");</div>
<h3>&nbsp;&nbsp;&nbsp; 生成的汇编码：</h3>
<div><font face="SimSun,宋体,MS Song,serif">
<p>&nbsp;long ixx = 0;<br>0104171E&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ixx],0 </p>
<p>&nbsp;tagTest tag1={34,6,87};<br>01041738&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [tag1],22h&nbsp;&nbsp;&nbsp;&nbsp; ; 成员赋值<br>0104173F&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-10h],6&nbsp;&nbsp;&nbsp; ; 成员赋值<br>01041746&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-0Ch],57h&nbsp; ; 成员赋值<br>&nbsp;ixx = funtest1(tag1,i2,"asdffffffdddddd");<br>0104174D&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset CAnonymousAsmTestApp::`vftable'+0F4h (11E60B0h) ; 入栈参数 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; "asdffffffdddddd" 的地址。这里显示似乎有问题（实际地址是对的）<br>01041752&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edx,dword ptr [i2]&nbsp; <br>01041755&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 入栈参数 i2<br>01041756&nbsp; sub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,0Ch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 在栈中分配参数 tag1 的空间<br>01041759&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,esp <br>0104175B&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,dword ptr [tag1]<br>0104175E&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [eax],ecx&nbsp; ; 入栈 tag1.n1<br>01041760&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edx,dword ptr [ebp-10h] <br>01041763&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [eax+4],edx&nbsp; ; 入栈 tag1.n2<br>01041766&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,dword ptr [ebp-0Ch] <br>01041769&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [eax+8],ecx&nbsp; ; 入栈 tag1.n3<br>0104176C&nbsp; call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; funtest1 (1041680h)&nbsp;&nbsp; ; 调用函数。</p>
<p><font color=#ff0000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;注意：这里同时将返回地址（下条指令的地址） 也入栈（这里是4字节）;</font></p>
<p><font color=#ff0000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp;&nbsp; 所以，函数中取得参数时，需要从当前 ESP 中加上 4 字节！</font><br>01041771&nbsp; add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,14h&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 由调用者清参数栈<br>01041774&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ixx],eax&nbsp; ; 获取返回值</p>
<p>&nbsp;</p>
</font><font face="SimSun,宋体,MS Song,serif"></div>
<div>
<div><font color=#ff0000>注意：已经关闭了编译器的某些优化选项，使汇编码更易读！</font></font></div>
</div>
<h3>&nbsp;&nbsp;&nbsp; 调用方代码总结：</h3>
<ul>
    <li>
    <div>参数从右向左依次入栈。</div>
    <li>
    <div>栈指针（ESP）从底部向顶部依次减小。也就是说，栈顶地址小；栈底地址大。</div>
    <li>如果参数是结构（struct），则直接移动栈顶指针，预留出结构的大小；然后用 mov 指令逐步将成员拷贝到预留出来的栈空间！
    <li>
    <div>call 指令调用目标地址</div>
    <li>
    <div>调用方清栈</div>
    <li>
    <div>获得返回值</div>
    </li>
</ul>
<h3>&nbsp;&nbsp;&nbsp; 函数中生成的汇编码</h3>
<div><font face="SimSun,宋体,MS Song,serif"><font color=#0000ff>long funtest1(tagTest p1,int p2,LPCTSTR lpszP3)<br>{</font><br>
<p>&nbsp;</p>
<p>00351680&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;&nbsp; ; 基址指针入栈<br>00351681&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp,esp&nbsp; ; 将函数入口点的栈指针作为当前基址指针。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;顾名思义，基址指针就是进入函数时的栈顶指针！<br>00351683&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx&nbsp;&nbsp;&nbsp;&nbsp; ; 在栈中分配局部变量空间。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;ecx 中的值无关紧要，只是预留一个 4 字节空间而已<br>&nbsp;p1.n1 = 3;<br>00351684&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [p1],3&nbsp; ; 赋值<br>&nbsp;LPCTSTR lpszxx = lpszP3;<br>0035168B&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,dword ptr [lpszP3] ; [lpszP3] 地址应该就是 ebp - 4<br>0035168E&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [lpszxx],eax ; 赋值（通过 eax）<br>&nbsp;p1.n3 = p2;<br>00351691&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,dword ptr [p2] <br>00351694&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp+10h],ecx ; 赋值（通过 ecx）<br>&nbsp;return 300;<br>00351697&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,12Ch&nbsp;&nbsp;&nbsp; ; 返回值放在 eax<br><font color=#0000ff>}<br></font>0035169C&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,ebp&nbsp; ; 恢复栈：清除局部变量<br>0035169E&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 恢复基址指针<br>0035169F&nbsp; ret&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回（<font color=#ff0000>从栈中弹出返回地址，然后 jmp</font> ）</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h2>2、_stdcall调用。</h2>
<h3 dir=ltr style="MARGIN-RIGHT: 0px">&nbsp;&nbsp;&nbsp; 调用方 C++ 代码：</h3>
<div>
<p>&nbsp;int i2=3;<br>&nbsp;long ixx = 0;</p>
<p>&nbsp;tagTest tag1={34,6,87};</p>
<p>&nbsp;ixx = funtest2(tag1,i2,"asdffffffdddddd");<br></p>
&nbsp;&nbsp;&nbsp; 生成的汇编码：</div>
<div><font face="SimSun,宋体,MS Song,serif">
<p>&nbsp;int i2=3;<br>010A1759&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [i2],3 <br>&nbsp;long ixx = 0;<br>010A1760&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ixx],0 </p>
<p>&nbsp;tagTest tag1={34,6,87};<br>010A1767&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [tag1],22h <br>010A176E&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-0Ch],6 <br>010A1775&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-8],57h </p>
<p>&nbsp;ixx = funtest2(tag1,i2,"asdffffffdddddd");<br>010A177C&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset CAnonymousAsmTestApp::`vftable'+104h (12460C0h) ; 入栈参数 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; "asdffffffdddddd" 的地址。这里显示似乎有问题（实际地址是对的）</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 和 cdecl 一样，参数从右边开始入栈<br></p>
<p><br>010A1781&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,dword ptr [i2] <br>010A1784&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax&nbsp; <br>010A1785&nbsp; sub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,0Ch <br>010A1788&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,esp <br>010A178A&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edx,dword ptr [tag1] <br>010A178D&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ecx],edx <br>010A178F&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,dword ptr [ebp-0Ch] <br>010A1792&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ecx+4],eax <br>010A1795&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edx,dword ptr [ebp-8] <br>010A1798&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ecx+8],edx <br>010A179B&nbsp; call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; funtest2 (10A16B0h) <br>010A17A0&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ixx],eax&nbsp; ; 取得返回值。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; <font color=#ff0000>注意：这里没有清栈的代码，由函数自己清栈</font>！</p>
<p>&nbsp;</p>
</font><font face="SimSun,宋体,MS Song,serif"></div>
<div>
<div><font color=#ff0000>注意：已经关闭了编译器的某些优化选项，使汇编码更易读！</font></font></div>
</div>
<h3>&nbsp;&nbsp;&nbsp; 调用方代码总结：</h3>
<ul>
    <li>
    <div>参数从右向左依次入栈。</div>
    <li>
    <div>栈指针（ESP）从底部向顶部依次减小。也就是说，栈顶地址小；栈底地址大。</div>
    <li>如果参数是结构（struct），则直接移动栈顶指针，预留出结构的大小；然后用 mov 指令逐步将成员拷贝到预留出来的栈空间！
    <li>
    <div>call 指令调用目标地址</div>
    <li>
    <div>返回时，由函数自己清栈（通过 ret 指令）</div>
    <li>
    <div>获得返回值</div>
    </li>
</ul>
<h3>&nbsp;&nbsp;&nbsp; 函数中生成的汇编码</h3>
<div><font face="SimSun,宋体,MS Song,serif"><font color=#0000ff>
<p><font color=#000000><font color=#0000ff>long __stdcall funtest2(tagTest p1,int p2,LPCTSTR lpszP3)<br>{<br></font>010A16B0&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp; <br>010A16B1&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp,esp <br>010A16B3&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx&nbsp;&nbsp;<br>&nbsp;p1.n1 = 3;<br>010A16BD&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [p1],3 <br>&nbsp;LPCTSTR lpszxx = lpszP3;<br>010A16C4&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,dword ptr [lpszP3] <br>010A16C7&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [lpszxx],eax <br>&nbsp;p1.n3 = p2;<br>010A16CA&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ecx,dword ptr [p2] <br>010A16CD&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp+10h],ecx </font></p>
<p><font color=#000000>&nbsp;return 300;<br>010A16D0&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,12Ch <br><font color=#0000ff>}</font><br>010A16D5&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,ebp <br>010A16D7&nbsp; pop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ebp&nbsp; <br>010A16D8&nbsp; ret&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 14h&nbsp;&nbsp;&nbsp; ; 返回并清栈。</font></p>
<p><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;</font><font color=#ff0000>（从栈中弹出返回地址，返回；然后ESP增加14H，14H为参数栈的字节数）</font></p>
</font></font></div>
</font></div>
<img src ="http://www.cppblog.com/flyinghare/aggbug/93315.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyinghare/" target="_blank">会飞的兔子</a> 2009-08-14 15:06 <a href="http://www.cppblog.com/flyinghare/archive/2009/08/14/93315.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>