﻿<?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++博客-luqingfei@C++-随笔分类-Win32汇编程语言序设计</title><link>http://www.cppblog.com/luqingfei/category/14442.html</link><description>&lt;font color='gray'&gt;世界上只有一种失败，那就是半途而废。&lt;/font&gt;~~
&lt;font color='gray'&gt;天道酬勤。&lt;/font&gt;~~
&lt;font color='gray'&gt;苦我心志，劳我筋骨。&lt;/font&gt;~~
&lt;b&gt;&lt;font color='black'&gt;……&lt;/font&gt;&lt;/b&gt;</description><language>zh-cn</language><lastBuildDate>Sat, 25 Sep 2010 11:13:32 GMT</lastBuildDate><pubDate>Sat, 25 Sep 2010 11:13:32 GMT</pubDate><ttl>60</ttl><item><title>Win32汇编--图形操作--GDI原理</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/24/127502.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Fri, 24 Sep 2010 05:51:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/24/127502.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/127502.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/24/127502.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/127502.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/127502.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>Win32</span><span>汇编</span><span>--</span><span>图形操作</span><span>--GDI</span><span>原理</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>是基于图形界面的，所以在</span><span>Win32</span><span>编程中，图形操作是最常用的操作。</span><span>GDI</span><span>的意义在于将程序对图形界面的操作和硬件设备隔绝开来，在程序中可以将所有的图形设备都看成是虚拟设备，包括视频显示器和打印机等，然后通过</span><span>GDI</span><span>函数用同样的方法去操作它们，由</span><span>Windows</span><span>负责将函数调用转化成针对具体硬件的操作。只要一个设备提供了和</span><span>Windows</span><span>兼容的驱动程序，它就可以被看做是一个标准的设备。以前在</span><span>DOS</span><span>系统下写应用程序的时候，如果要进行图形操作，那么就要考虑到市场上每种显示卡的不同，否则在装配某种显卡的计算机上就可能无法正常运行，对汇编程序员来说，这真是一个恶梦。在</span><span>Win32</span><span>编程中，正是</span><span>GDI</span><span>函数让这个恶梦成为历史。</span></p>
<p>&nbsp;</p>
<p><span>GDI</span><span>函数全部包括在</span><span>GDI32.DLL</span><span>中，在编程的时候，注意要在源程序的开头加上相应的包含语句：</span></p>
<p><span>include <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>gdi32.inc</span></p>
<p><span>includelib <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>gdi32.lib</span></p>
<p>&nbsp;</p>
<p><span>和</span><span>GDI</span><span>相关的内容真是太庞大了，只要查看一下</span><span>gdi32.inc</span><span>文件就可以发现，函数的总数达到了</span><span>300</span><span>多个，和</span><span>GDI</span><span>相关的数据结构也非常多，为了能了解</span><span>GDI</span><span>的原理和基本的使用方法：</span></p>
<p><span>归纳起来，</span><span>GDI</span><span>操作可以从</span><span>3</span><span>个方面去了解——</span><span>When, Where</span><span>和</span><span>How</span><span>：</span></p>
<p><span>When</span><span>——指的是进行图形操作的时机，究竟什么时刻最适合程序进行图形操作呢？——&#8220;</span><span>GDI</span><span>程序的结构&#8221;</span></p>
<p><span>Where</span><span>——指的是图形该往哪里画，既然</span><span>Windows</span><span>隔离了硬件图形设备，那么该把什么地方当做&#8220;下笔&#8221;的地方呢？——&#8220;设备环境&#8221;</span></p>
<p><span>How</span><span>——了解了上面两个问题后，最后还要知道&#8220;如何画&#8221;，这就涉及如何使用大部分</span><span>GDI</span><span>函数的问题了。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>一、</span><span>GDI</span><span>程序的结构</span></p>
<p><span>1</span><span>、客户区的刷新</span></p>
<p><span>正如上面所说的，这里讨论的是&#8220;</span><span>When</span><span>&#8221;的问题，读者可能会问：为什么会有这个问题，如果要向窗口输出图形，程序想在什么时候输出那就是什么时候，难道这个时刻还有规定不成？</span></p>
<p>&nbsp;</p>
<p><span>在</span><span>DOS</span><span>操作系统中编程的时候，程序把文字或图形输出到屏幕，在输出新的内容之前，这些内容总是保留在屏幕原处，这些内容会被意外覆盖的唯一情况是激活一个</span><span>TSR</span><span>程序，但</span><span>TSR</span><span>程序在退出之前有义务恢复原来的屏幕，如果它无法恢复屏幕的内容，那么这是它的责任，我们不会在自己的程序中去考虑屏幕内容会无缘无故消失这种情况，所以可以把屏幕看成是应用程序私有的。</span></p>
<p>&nbsp;</p>
<p><span>如果程序输出的内容过多，如用</span><span>dir</span><span>显示一个含有很多文件的目录，用户根本无法看清快速上翻的屏幕，这时程序可以设计一个参数来暂停一下，如</span><span>dir/p</span><span>。这已经是</span><span>DOS</span><span>程序最&#8220;体贴&#8221;的做法了，如果用户想回过头去看已经滚出屏幕的内容，那可对不起，只能再执行一遍了！</span></p>
<p>&nbsp;</p>
<p><span>所以对</span><span>DOS</span><span>程序来说，程序想在什么时候输出信息那就是什么时候，根本不存在</span><span>When</span><span>这个问题。</span></p>
<p>&nbsp;</p>
<p><span>但在</span><span>Windows</span><span>操作系统中，屏幕是多个程序&#8220;公用&#8221;的，用户程序不要指望输出到窗口中的内容经过一段时间后还会保留在那里，它们可能被别的东西覆盖，如其他窗口、鼠标箭头或下拉的菜单等。在</span><span>Windows</span><span>中，恢复被覆盖内容的责任大部分属于用户程序自己，理由很简单：</span><span>Windows</span><span>是个多任务的操作系统，假如程序</span><span>B</span><span>覆盖了程序</span><span>A</span><span>的窗口内容，覆盖掉的内容由程序</span><span>B</span><span>负责恢复的话，它就必须保存它覆盖掉的内容，但是在它将保存的内容恢复之前，程序</span><span>A</span><span>也在运行，并可能在程序</span><span>B</span><span>恢复以前已经向它自己的窗口输出新的内容，结果当程序</span><span>B</span><span>恢复它保存的窗口内容时，保存的内容可能是过时的（而</span><span>DOS</span><span>的情况就不同，</span><span>TSR</span><span>程序激活的时候，用户程序是被挂起的），所以最好的办法就是让程序</span><span>A</span><span>自己来决定如何恢复。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>系统采用的方法是：当</span><span>Windows</span><span>检测到窗口被覆盖的地方需要恢复的时候，它会向用户程序发送一个</span><span>WM_PAINT</span><span>消息，消息中包括了需要恢复的区域，然后由用户程序来决定如何恢复被覆盖的内容。</span></p>
<p>&nbsp;</p>
<p><span>如果程序因为忙于处理其他事务以至于无法及时响应</span><span>WM_PAINT</span><span>消息，那么窗口客户区原先被覆盖的地方可能会被</span><span>Windows</span><span>暂时画成一块白色（或者背景色）的矩形，或者根本就是保留被覆盖时的情形，直到程序有时间去响应</span><span>WM_PAINT</span><span>消息为止。我们常常可以看到这种情况发生在死锁程序的客户区内，这就是因为死锁的程序无法响应</span><span>WM_PAINT</span><span>消息来恢复客户区造成的。</span></p>
<p>&nbsp;</p>
<p><span>所以对于&#8220;</span><span>When</span><span>&#8221;这个问题，答案是：程序应该在</span><span>Windows</span><span>要求的时候绘画客户区，也就是在收到</span><span>WM_PAINT</span><span>消息的时候。如果程序需要主动刷新客户区，那么可以通过调用</span><span>InvalidateRect</span><span>等函数引发一条</span><span>WM_PAINT</span><span>消息，因为在</span><span>WM_PAINT</span><span>消息中刷新客户区的代码是必须存在的，所以用这种看似&#8220;舍近求远&#8221;的办法实际上可以节省一份重复的代码。即使是在游戏程序这种&#8220;主动刷新&#8221;远远多于&#8220;被动刷新&#8221;的程序中，只要窗口有被其他东西覆盖的可能，那么这个原则就是适用的。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、</span><span>GDI</span><span>程序的结构</span></p>
<p><span>对于</span><span>Win32</span><span>程序来说，</span><span>WM_PAINT</span><span>消息随时可能发生，这就意味着，程序再也不能像在</span><span>DOS</span><span>下一样输出结果后就不管了，反过来，程序在任何时刻都应该知道如何恢复整个或局部客户区中以前输出的内容。</span></p>
<p>&nbsp;</p>
<p><span>如果程序的功能比较简单，可以将计算及刷新整个客户区的代码全部安排在</span><span>WM_PAINT</span><span>消息中完成，这样，每次当客户区的全部或部分需要被更新的时候，程序重新执行整个生成客户区屏幕数据的功能模块并刷新客户区。这种结构适用于功能模块很短小且执行速度很快的情况，整个过程的时间最好不超过几百</span><span>ms</span><span>，否则，用户会在一个明显的等待时间后才看到程序把客户区中的&#8220;空洞&#8221;补上。</span></p>
<p>&nbsp;</p>
<p><span>当生成屏幕数据的功能模块有些复杂的时候，就应该考虑采用如下结构，即功能模块和客户区刷新模块分别在不同的子程序中实现，功能模块单独用一个子程序完成，这个子程序可以由用户通过选择菜单项在</span><span>WM_COMMAND</span><span>消息中执行，也可以新建另外一个线和来完成，总之，它最后把计算结果放到一个缓冲区中，而每当客户区需要刷新时，程序在</span><span>WM_PAINT</span><span>消息中调用客户区刷新子程序，这个子程序从计算好的缓冲区中取出数据并输出到客户区中，由于单纯的屏幕刷新过程是很快的，所以用户根本来不及看到客户区中的空洞。</span></p>
<p>&nbsp;</p>
<p><span>3</span><span>、探讨</span><span>WM_PAINT</span><span>消息</span></p>
<p><span>当客户区被覆盖并重新显示的时候，</span><span>Windows</span><span>并不是在所有的的下都发送</span><span>WM_PAINT</span><span>消息，下面是几种不同的情况：</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>当鼠标光标移过窗口客户区以及图标拖过客户区这两种情况，</span><span>Windows</span><span>总是自己保存被覆盖的区域并恢复它，并不需要发送</span><span>WM_PAINT</span><span>消息通知用户程序。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>当窗口客户区被自己的下拉式菜单覆盖，或者被自己弹出的对话框覆盖后，</span><span>Windows</span><span>会尝试保存被覆盖的区域并在以后恢复它，如果因为某种原因无法保存并恢复的话，</span><span>Windows</span><span>会发送一个</span><span>WM_PAINT</span><span>消息通知程序。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>别的情况造成窗口的一部分从不可见变到可见，如程序从最小化的状态恢复，其他的窗口覆盖客户区后移开，用户改变了窗口的大小不一和用户按动滚动条等，在这些情况下，</span><span>Windows</span><span>会向窗口发送</span><span>WM_PAINT</span><span>消息。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>一些函数会引发</span><span>WM_PAINT</span><span>消息，如</span><span>UpdateWindow</span><span>，</span><span>InvalidateRect</span><span>以及</span><span>InvalidateRgn</span><span>函数等。</span></p>
<p>&nbsp;</p>
<p><span>窗口过程收到</span><span>WM_PAINT</span><span>消息后，并不代表整个客户区都需要被刷新，有可能客户区被覆盖的区域只有一小块，这个区域就叫做&#8220;无效区域&#8221;，程序只需要更新这个区域。</span></p>
<p>&nbsp;</p>
<p><span>和</span><span>WM_TIMER</span><span>消息类似，</span><span>WM_PAINT</span><span>消息也是一个低级别的消息，虽然它不会像</span><span>WM_TIMER</span><span>消息一样被丢弃，但</span><span>Windows</span><span>总是在消息循环空的时候才把</span><span>WM_PAINT</span><span>放入其中，实际上，</span><span>Windows</span><span>为每个窗口维护一个&#8220;绘图信息结构&#8221;，无效区域的坐标就在其中，每当消息循环空的时候，如果</span><span>Windows</span><span>发现存在一个无效区域，就会放入一个</span><span>WM_PAINT</span><span>消息。</span></p>
<p>&nbsp;</p>
<p><span>无效区域的坐标并不附带在</span><span>WM_PAINT</span><span>消息的参数中，在程序中有其他方法可以获取，</span><span>WM_PAINT</span><span>消息只是通知程序有个区域需要更新而已，所以</span><span>Windows</span><span>也不会同时将两条</span><span>WM_PAINT</span><span>消息放入消息循环，当</span><span>Windows</span><span>要放入一条</span><span>WM_PAINT</span><span>消息的时候，如果发现已存在一个无效区域了，那么它只需要把新旧两个无效区域合并计算出一个新的无效区域就可以了，消息循环中还是只需要一条</span><span>WM_PAINT</span><span>消息。</span></p>
<p>&nbsp;</p>
<p><span>由于存在&#8220;无效区域&#8221;这样一个东西，所以程序在</span><span>WM_PAINT</span><span>消息中对客户区刷新完毕后工作并没有结束，如果不使无效区域变得有效，</span><span>Windows</span><span>会在下一轮消息循环中继续放入一个</span><span>WM_PAINT</span><span>消息。当</span><span>Windows</span><span>检查&#8220;绘图信息结构&#8221;的时候发现没有了无效区域，也就不会继续发送</span><span>WM_PAINT</span><span>消息了。</span></p>
<p>&nbsp;</p>
<p><span>WM_PAINT</span><span>消息的处理流程一般是：</span></p>
<p><span>.if<span>&nbsp;&nbsp;&nbsp; </span>eax == WM_PAINT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;eax</span><span>为</span><span>uMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke BeginPaint, hWnd, addr stPS</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>刷新客户区的代码</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke EndPaint, hWnd, addr stPS</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>xor&nbsp;eax, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>读者可以发现中间并没有调用</span><span>ValidateRect</span><span>来使无效区域变得有效，这是因为</span><span>BeginPaint</span><span>函数和</span><span>EndPaint</span><span>函数隐含有这个功能，如果不是以</span><span>BeginPaint/EndPaint</span><span>当做消息处理代码的头尾的话，那么在</span><span>WM_PAINT</span><span>消息返回的时候就必须调用</span><span>ValidateRect</span><span>函数。</span></p>
<p>&nbsp;</p>
<p><span>BeginPaint</span><span>函数的第二个参数是一个绘图信息结构的缓冲区地址，</span><span>Windows</span><span>会在这里返回绘图信息结构，结构中包含了无效区域的位置和大小，绘图信息结构的定义如下：</span></p>
<p><span>PAINTSTRUCT STRUCT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hdc<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DWORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fErase<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DWORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rcPaint<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>RECT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fRestore<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DWORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fIncUpdate<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>DWORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rgbReserved<span>&nbsp;&nbsp; </span>BYTE 32 dup(?)</span></p>
<p><span>PAINTSTRUCT ENDS</span></p>
<p>&nbsp;</p>
<p><span>其中</span><span>hdc</span><span>字段是窗口的设备环境句柄，</span><span>rcPaint</span><span>字段是一个</span><span>RECT</span><span>结构，它指定了无效区域矩形的对角顶点，</span><span>fErase</span><span>字段如果为非零值，表示</span><span>Windows</span><span>在发送</span><span>WM_PAINT</span><span>消息前已经用背景色擦除了无效区域，后面</span><span>3</span><span>个字段是</span><span>Windows</span><span>内部使用的，应用程序不必去理会它们。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>二、设备环境</span></p>
<p><span>解决了&#8220;</span><span>When</span><span>&#8221;的总是后，再考虑一下&#8220;</span><span>Where</span><span>&#8221;的问题。</span></p>
<p><span>在</span><span>DOS</span><span>操作系统中，向屏幕输出数据实际上是把输出内容拷贝到视频缓冲区中，如果在文本模式下显示信息，只需要把内容拷贝到</span><span>B8000h</span><span>处的内存中；显示图形信息，可以把图形数据拷贝到</span><span>A0000h</span><span>处的内存中。</span></p>
<p>&nbsp;</p>
<p><span>在</span><span>Windows</span><span>中，</span><span>GDI</span><span>接口把程序和硬件分隔出来，在</span><span>Win32</span><span>编程中，再也不能通过直接向视频缓冲区拷贝数据的办法来显示信息了，那么，究竟该往哪里输出图形呢——这就是&#8220;</span><span>Where</span><span>&#8221;的问题。答案是：通过&#8220;设备环境&#8221;来输出图形。</span></p>
<p><span>1</span><span>、什么是设备环境</span></p>
<p><span>在</span><span>Windows</span><span>中，所有与图形相关的操作都是用统一的方法来完成的（不然就不能称为&#8220;图形设备接口&#8221;了）。不管是绘画屏幕上的一个窗口，还是把图形输出到打印机，或者对一幅位图进行绘画，使用的绘图函数都是相同的，为了实现方法上的统一，必须将所有的图形对象看成是一个虚拟的设备，这些设备可能有不同的属性，如黑白打印机和彩色屏幕的颜色深度是不同的，不同打印机的尺寸和分辨率可能是不同的，绘图仪只支持矢量而不支持位图等。不同设备的不同属性就构成了一个绘图的&#8220;环境&#8221;，就像</span><span>DOS</span><span>操作系统中把视频缓冲区当做图形操作的对象一样，这个绘图的&#8220;环境&#8221;就是</span><span>Win32</span><span>编程中图形操作的对象，把它叫做&#8220;设备环境&#8221;。设备环境实际上是一个数据结构，结构中保存的就是设备的属性，当对设备环境进行图形操作的时候，</span><span>Windows</span><span>可以根据这些属性找到对应的设备进行相关的操作。</span></p>
<p>&nbsp;</p>
<p><span>在实际使用中，通过&#8220;设备环境&#8221;可以操作的对象很广泛，除了可以是打印机或绘图仪等硬件设备外，也可以是窗口的客户区，包括大大小小的所有可以被称为窗口的按钮与控件等的客户区，也可以是一个位图。总之，任何需要用到图形操作的东西都可以通过&#8220;设备环境&#8221;进行绘图。</span></p>
<p>&nbsp;</p>
<p><span>为了更好地理解&#8220;设备环境&#8221;是什么，先来看一个例子：</span></p>
<p><span>//DcCopy.asm</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.386</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.model flat, stdcall</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>option casemap:none</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; Include </span><span>文件定义</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>windows.inc</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>gdi32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>gdi32.lib</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.lib</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.lib</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>ID_TIMER<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>数据段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.data?</span></p>
<p><span>hInstance<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>hWin1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>hWin2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.const</span></p>
<p><span>szClass1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'SourceWindow',0</span></p>
<p><span>szClass2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>'DestWindow',0</span></p>
<p><span>szCaption1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'</span><span>请尝试用别的窗口覆盖本窗口</span><span>!',0</span></p>
<p><span>szCaption2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'</span><span>本窗口图像拷贝自另一窗口</span><span>',0</span></p>
<p><span>szText<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'Win32 Assembly, Simple and powerful!',0</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.code</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>定时器过程</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>_ProcTimer<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp; </span>_hWnd, uMsg, _idEvent, _dwTime</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@hDc1, @hDc2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@stRect:RECT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetDC, hWin1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@hDc1, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetDC, hWin2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@hDc2,eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetClientRect, hWin1, addr @stRect</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;BitBlt, @hDc2, 0, 0, @stRect.right, @stRect.bottom, @hDc1, 0, 0, SRCCOPY</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;ReleaseDC, hWin1, @hDc1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;ReleaseDC, hWin2, @hDc2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>_ProcTimer<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>窗口过程</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>_ProcWinMain<span>&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp; </span>uses ebx edi esi, hWnd, uMsg, wParam, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@stPs:PAINTSTRUCT</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@stRect:RECT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@hDc</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, uMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ecx, hWnd</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_PAINT &amp;&amp; ecx == hWin1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke BeginPaint, hWnd, addr @stPs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@hDc, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetClientRect, hWnd, addr @stRect</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;DrawText, @hDc, addr szText, -1, addr @stRect, DT_SINGLELINE or DT_CENTER or DT_VCENTER</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;EndPaint, hWnd, addr @stPs</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif eax == WM_CLOSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;PostQuitMessage, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;DestroyWindow, hWin1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;DestroyWindow, hWin2</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.else</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;DefWindowProc, hWnd, uMsg, wParam, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>xor eax, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>_ProcWinMain<span>&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>_WinMain proc</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@stWndClass:WNDCLASSEX</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@stMsg:MSG</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@hTimer</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetModuleHandle, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>hInstance, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;RtlZeroMemory, addr @stWndClass, sizeof @stWndClass</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp; </span>LoadCursor, 0, IDC_ARROW</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.hCursor, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>hInstance</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.hInstance</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.cbSize, sizeof WNDCLASSEX</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.style, CS_HREDRAW or CS_VREDRAW</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.lpfnWndProc, offset _ProcWinMain</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.hbrBackground, COLOR_WINDOW + 1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.lpszClassName, offset szClass1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp; </span>RegisterClassEx, addr @stWndClass</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp; </span>CreateWindowEx, WS_EX_CLIENTEDGE, offset szClass1, offset szCaption1, WS_OVERLAPPEDWINDOW, 450, 100, 300, 300, NULL, NULL, hInstance, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hWin1, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span>invoke<span>&nbsp;&nbsp; </span>ShowWindow, hWin1, SW_SHOWNORMAL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp; </span>UpdateWindow, hWin1</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.lpszClassName, offset szClass2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;RegisterClassEx, addr @stWndClass</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;CreateWindowEx, WS_EX_CLIENTEDGE, offset szClass2, offset szCaption2, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, NULL, NULL, hInstance, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>hWin2, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;ShowWindow, hWin2, SW_SHOWNORMAL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;UpdateWindow, hWin2</span></p>
<p><span>;****************************************************************</span></p>
<p><span>; </span><span>设置定时器</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;SetTimer, NULL, NULL, 100, addr _ProcTimer</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@hTimer, eax</span></p>
<p><span>;****************************************************************</span></p>
<p><span>; </span><span>消息循环</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.while TRUE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetMessage, addr @stMsg, NULL, 0, 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.break&nbsp;.if eax == 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;invoke&nbsp;TranslateMessage, addr @stMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;DispatchMessage, addr @stMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endw</span></p>
<p><span>;****************************************************************</span></p>
<p><span>; </span><span>清除定时器</span></p>
<p><span>;****************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;KillTimer, NULL, @hTimer</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>_WinMain endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>start:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call<span>&nbsp;&nbsp;&nbsp; </span>_WinMain</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;ExitProcess, NULL</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>end start</span></p>
<p><span>这个程序的代码用到的大部分知识都是前面已经讲到的，在</span><span>_WinMain</span><span>中，用一个同样的窗口类建立了两个窗口，两个窗口属于同一个窗口类，所以它们的窗口过程都是</span><span>_ProcWinMain</span><span>，为了关闭任何一个窗口都可以结束程序，</span><span>WM_CLOSE</span><span>消息中用</span><span>DestroyWindow</span><span>函数摧毁了两个窗口。程序设置了一个周期为</span><span>100ms</span><span>的定时器，</span><span>Windows</span><span>会每隔</span><span>100ms</span><span>调用</span><span>_ProcTimer</span><span>子程序。在</span><span>_ProcTimer</span><span>中，将其中一个窗口的客户区拷贝到另一个窗口的客户区中，方法是通过</span><span>GetDC</span><span>获取窗口的</span><span>DC</span><span>句柄，并用</span><span>BitBlt</span><span>函数完成拷贝工作，所以在右边的窗口显示了一句&#8220;</span><span>Win32 Assembly, Simple and powerful!</span><span>&#8221;，左边的窗口中也会出现这句话。</span></p>
<p>&nbsp;</p>
<p><span>程序每</span><span>100ms</span><span>将右边窗口的客户区拷贝到左边的窗口客户区中，通过左边窗口的客户区就可以了解右边客户区的</span><span>DC</span><span>对应的究竟是什么内容。</span></p>
<p>&nbsp;</p>
<p><span>通过左边窗口的变化可以惊奇地发现：右边窗口客户区的内容并不是程序自己输出到客户区的那句文本，而是以客户区为矩形区域的屏幕上我们真正看到的东西，它竟然包括其他窗口覆盖在上面的东西。这就意味着，扫雷游戏和纸牌游戏通过自己客户区对应的设备环境画图形，图形数据竟然画到了</span><span>DcCopy</span><span>窗口客户区对应的设备环境中。</span></p>
<p>&nbsp;</p>
<p><span>这个例子验证了<strong>&#8220;设备环境&#8221;只是&#8220;环境&#8221;而不是&#8220;设备&#8221;，它并不存储发给它的图形数据，图形数据透过它写到了它所描述的&#8220;设备&#8221;上，每个窗口客户区的&#8220;设备环境&#8221;对应的设备都是屏幕，但它们在位置上可能重叠，所以向一个窗口的客户区写数据相当于同时写了下层窗口的客户区。</strong></span></p>
<p>&nbsp;</p>
<p><span>为了让当前激活的窗口在视觉上保持在最上面，下层窗口向自己客户区写的内容首先要经过</span><span>Windows</span><span>的&#8220;过滤&#8221;，只有没有被其他窗口覆盖掉的部分才真正被写到了屏幕上。</span></p>
<p>&nbsp;</p>
<p><span>读者应该时刻提醒自己——&#8220;设备环境&#8221;只是一个环境，是设备属性的一组定义，程序输出的图形数据透过&#8220;设备环境&#8221;被定向到了具体的设备上，&#8220;设备环境&#8221;本身并不存储这些数据。</span></p>
<p>&nbsp;</p>
<p><span>Device Context</span><span>中</span><span>Context</span><span>的含义：设备环境的上面是应用程序，下面是具体设备，而它是用来&#8220;联系上下关系&#8221;用的。</span></p>
<p>&nbsp;</p>
<p><span>有人可能认为：屏幕上的窗口就像放在桌面上的一张张纸，虽然一张纸可能暂时被另一张遮住，但纸上写的东西还是存在的，移开另一张纸就可以再次露出来。但实际情况是：桌面更像一个用粉笔写的公告黑板，一个窗口相当于划了一块空间写告示，写另一个告示的时候要把老告示的内容擦去一部分以便写新的内容，擦去的东西也就不存在了，如果要恢复老告示，那么必须把擦去的部分重新写上去。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、获取设备环境句柄</span></p>
<p><span>要想对任何设备绘图，首先必须获取设备的&#8220;设备环境句柄&#8221;（</span><span>hDC</span><span>），几乎所有的</span><span>GDI</span><span>函数的操作目标都是</span><span>hDC</span><span>，在程序中得到一个</span><span>hDC</span><span>有几种方法。</span></p>
<p>&nbsp;</p>
<p><span>最常用的方法是在</span><span>WM_PAINT</span><span>消息中用</span><span>BeginPaint</span><span>函数得到</span><span>hDC</span><span>，</span><span>WM_PAINT</span><span>消息的代码结构一般是：</span></p>
<p><span>.if<span>&nbsp;&nbsp;&nbsp; </span>eax == WM_PAINT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;eax</span><span>为</span><span>uMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke BeginPaint, hWnd, addr stPS</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>刷新客户区的代码</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke EndPaint, hWnd, addr stPS</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>xor eax, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>BeginPaint</span><span>函数的返回值就是需要刷新区域的</span><span>hDC</span><span>。要注意的是：</span><span>BeginPaint</span><span>返回的</span><span>hDC</span><span>对应的尺寸仅是无效区域，无法用它绘画到这个区域以外的地方去。由于窗口过程每次接收</span><span>WM_PAINT</span><span>消息时的无效区域可能是不同的，所以这个</span><span>hDC</span><span>的值仅在</span><span>WM_PAINT</span><span>消息中有效，程序不应该保存它并把它用在</span><span>WM_PAINT</span><span>消息以外的代码中。基于同样的道理，</span><span>BeginPaint</span><span>和</span><span>EndPaint</span><span>函数只能用在</span><span>WM_PAINT</span><span>消息中，因为只有这时候才存在无效区域。</span></p>
<p>&nbsp;</p>
<p><span>程序中常常有这种需求，就是在非</span><span>WM_PAINT</span><span>消息中主动绘画客户区，由于</span><span>BeginPaint</span><span>和</span><span>EndPaint</span><span>函数必须在</span><span>WM_PAINT</span><span>消息中使用，所以这时必须用另外的方法获取</span><span>hDC</span><span>，可以使用以下的方法：</span></p>
<p><span>invoke GetDC, hWnd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>获取</span><span>hDC</span></p>
<p><span>;</span><span>返回值是</span><span>hDC</span></p>
<p><span>;</span><span>绘图代码</span></p>
<p><span>invoke ReleaseDC, hWnd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>释放</span><span>hDC</span></p>
<p>&nbsp;</p>
<p><span>GetDC</span><span>函数返回的</span><span>hDC</span><span>对应窗口的整个客户区，当使用完毕的时候，</span><span>hDC</span><span>必须用</span><span>ReleaseDC</span><span>函数释放。对于用</span><span>GetDC</span><span>获取的</span><span>hDC</span><span>，</span><span>Windows</span><span>建议使用的范围限于单条消息内，当程序在处理某条消息的时候需要绘画客户区时，可以用</span><span>GetDC</span><span>获取</span><span>hDC</span><span>，但在消息返回前，必须用</span><span>ReleaseDC</span><span>将它释放掉，如果在下一条消息中需要继续使用到</span><span>hDC</span><span>，那么必须重新用</span><span>GetDC</span><span>函数获取。</span></p>
<p>&nbsp;</p>
<p><span>上面的两种方法获取的</span><span>hDC</span><span>都是窗口的</span><span>hDC</span><span>，如果要操作的是其他的东西，如打印机、位图等，就不能使用</span><span>BeginPaint</span><span>或</span><span>GetDC</span><span>函数了。当绘图的对象是一个设备的时候，可以用</span><span>CreateDC</span><span>函数来建立一个</span><span>DC</span><span>：</span></p>
<p><span>invoke CreateDC, lpszDriver, lpszDevice, lpszOutput, lpInitData</span></p>
<p><span>lpszDriver</span><span>指向设备名称，如显示设备的设备名是</span><span>DISPLAY</span><span>，打印机的设备名一般为</span><span>WINSPOOL</span><span>，下面这几句代码建立的</span><span>DC</span><span>对应整个屏幕：</span></p>
<p><span>szDriver<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp; </span>&#8220;DISPLAY&#8221;,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke CreateDC, addr szDriver, NULL, NULL, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov hDC, eax</span></p>
<p>&nbsp;</p>
<p><span>当绘图对象是位图的时候，同样需要一个和位图句柄相联系的</span><span>DC</span><span>，这时可以用函数</span><span>CreateCompatibleDC</span><span>来创建一个显示表面仅存在于内存中的</span><span>DC</span><span>：</span></p>
<p><span>invoke CreateCompatibleDC, hDC</span></p>
<p><span>参数中的</span><span>hDC</span><span>是用来参考的</span><span>DC</span><span>句柄，如果指定的参数是</span><span>NULL</span><span>，那么建立的</span><span>DC</span><span>将和当前屏幕的设置兼容，为了用</span><span>CreateCompatibleDC</span><span>建立的</span><span>DC</span><span>绘画一个位图，还需要用</span><span>SelectObject</span><span>函数将</span><span>hDC</span><span>和位图句柄联系起来。</span></p>
<p>&nbsp;</p>
<p><span>用</span><span>CreateDC</span><span>和</span><span>CreateCompatibleDC</span><span>函数建立的</span><span>hDC</span><span>在使用结束以后，必须用</span><span>DeleteDC</span><span>函数删除，注意这里不能用</span><span>ReleaseDC</span><span>，这个函数是和</span><span>GetDC</span><span>配合用的。</span></p>
<p>&nbsp;</p>
<p><span>用</span><span>BeginPaint/EndPaint</span><span>以及</span><span>GetDC</span><span>获取的</span><span>hDC</span><span>的使用时间不能超出本条消息，与此相比，用</span><span>CreateDC</span><span>以及</span><span>CreateCompatibleDC</span><span>建立的</span><span>hDC</span><span>就没有这个限制，可以在任何时刻建立它并且一直使用到不再需要为止。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>三、色彩和坐标</span></p>
<p><span>1</span><span>、</span><span>Windows</span><span>中的色彩</span></p>
<p><span>可以表示的颜色总数由颜色深度决定，也就是存储每个像素所用的位数，各种显示设备可以显示的颜色总数可能大不相同，如果设备支持的颜色深度太浅，就会影响到图像的质量，会让人看起来觉得很粗糙和不自然。</span></p>
<p>&nbsp;</p>
<p><span>一种颜色可以分解成红、绿、蓝三原色，所以可以用红、绿、蓝</span><span>3</span><span>个分量的组合来表示各种颜色。</span></p>
<p>&nbsp;</p>
<p><span>当设备支持的颜色深度少于等于</span><span>8</span><span>位时（如</span><span>8</span><span>位（</span><span>256</span><span>色）、</span><span>4</span><span>位（</span><span>16</span><span>色）、</span><span>2</span><span>位（</span><span>4</span><span>色）或</span><span>1</span><span>位（</span><span>2</span><span>色）），总体位数太少，不足以用来表达</span><span>3</span><span>个颜色分量，这时系统建立一个色彩表，像素数据用来做索引在色彩表中获取颜色值，所以低于</span><span>8</span><span>位的颜色称为索引色。</span></p>
<p>&nbsp;</p>
<p><span>只有当颜色深度大于</span><span>8</span><span>位的时候，像素数据中才直接包含红、绿、蓝</span><span>3</span><span>个分量。当颜色深度为</span><span>16</span><span>位的时候，红、绿、蓝各用</span><span>5</span><span>位表示，剩下的</span><span>1</span><span>位用做属性位，实际可以表示的颜色数目为</span><span>2^15=32 768</span><span>种，</span><span>16</span><span>位深度的彩色又称为</span><span>16</span><span>位色、高彩色或增强色。当颜色深度为</span><span>24</span><span>位的时候，</span><span>3</span><span>个分量各用</span><span>8</span><span>位表示，实际可以表示的颜色数目为</span><span>2^24=16777216</span><span>种，</span><span>24</span><span>位深度的彩色又称为</span><span>24</span><span>位色、</span><st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="16" UnitName="m"><span>16M</span></st1:chmetcnv><span>色或真彩色。对于人的眼睛来说，超过</span><span>16</span><span>位的颜色就已经很难分辨了。</span></p>
<p>&nbsp;</p>
<p><span>在</span><span>Win32</span><span>的编程中，统一使用</span><span>32</span><span>位的整数表示一个深度为</span><span>24</span><span>位的颜色，在这</span><span>32</span><span>位中只使用低</span><span>24</span><span>位，每一种原色分量占用</span><span>8</span><span>位，其中</span><span>0~7</span><span>位为红色，</span><span>8~15</span><span>位为绿色，</span><span>16~23</span><span>位为蓝色。在程序中用到一种颜色常数的时候，可以如下使用：</span></p>
<p><span>move ax, </span><span>红色</span><span> + </span><span>绿色</span><span>*100h + </span><span>蓝色</span><span>*10000h&nbsp;;</span><span>将颜色放入</span><span>eax</span><span>中</span></p>
<p>&nbsp;</p>
<p><span>当显示设备无法表示</span><span>24</span><span>位色的时候，</span><span>Windows</span><span>会自动用设备可以显示的最接近的颜色来代替它，当显示设备的颜色深度比较低的时候，可以通过函数</span><span>GetNearestColor</span><span>来得知一种颜色（颜色）会被系统替换成哪种颜色：</span></p>
<p><span>invoke GetNearestColor, hDC, dwColor<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>返回真正使用的颜色值</span></p>
<p>&nbsp;</p>
<p><span>但是当显示设备颜色深度太低的时候，经过</span><span>Windows</span><span>自动转换的图像可能会让人觉得很不自然，所以在有些时候，程序员可能希望预先得知设备的颜色深度，然后根据具体情况显示不同的图形。</span></p>
<p>&nbsp;</p>
<p><span>显示设备的颜色深度可以用以下函数获取：</span></p>
<p><span>invoke GetDeviceCaps, hDC, PLANES</span></p>
<p><span>mov ebx, dwPlanes</span></p>
<p><span>invoke GetDeviceCaps, hDC, BITSPIXEL</span></p>
<p><span>mul ebx</span></p>
<p><span>mov dwColorDepth, eax</span></p>
<p>&nbsp;</p>
<p><span>第一个函数调用返回</span><span>DC</span><span>的色彩平面数，第二个函数调用返回每个像素的色彩位数，颜色深度最后可以通过</span><span>dwPlanes</span><span>乘以</span><span>dwBitsPixel</span><span>得到。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>2</span><span>、</span><span>Windows</span><span>中的坐标系</span></p>
<p><span>要用</span><span>GDI</span><span>函数绘图，就必须首先了解这些函数使用的坐标系，在默认的状态下，</span><span>Windows</span><span>坐标系以左上角做坐标原点，以右方当做</span><span>X</span><span>坐标的正方向，以下方当做</span><span>Y</span><span>坐标的正方向。坐标的数值要用一个有符号的</span><span>16</span><span>位数来表示，范围从</span><span>-32 768~32767</span><span>，坐标的单位为像素。这种坐标系定义方法的好处是：窗口中每一点的坐标不会因为窗口的大小改变而改变，试想一下，如以数学中通常的表示方法，以左下角做坐标原点，那么当窗口高度被用户调整的时候，客户区中每一点的</span><span>Y</span><span>坐标都会变化，在具体使用中就会有诸多不便。</span></p>
<p>&nbsp;</p>
<p><span>但是</span><span>Windows</span><span>也提供了其他的一些坐标映射方法供程序员使用，可以用</span><span>SetMapMode</span><span>函数来为一个</span><span>DC</span><span>设置新的坐标映射方法：</span></p>
<p><span>invoke SetMapMode, hDC, iMapMode</span></p>
<p><span>可以设置的参数包括坐标原点、坐标和逻辑单位和坐标的正方向等，参数中的</span><span>iMapMode</span><span>为新的映射方式，其可以选择的取值如下表所示，</span><span>Windows</span><span>默认使用的映射方式为</span><span>MM_TEXT</span><span>。</span></p>
<p><span>Windows</span><span>中可用的坐标映射方式</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=139>
            <p><strong><span>映射方式</span></strong></p>
            </td>
            <td vAlign=top width=107>
            <p><strong><span>原点</span></strong></p>
            </td>
            <td vAlign=top width=107>
            <p><strong><span>逻辑单位</span></strong></p>
            </td>
            <td vAlign=top width=107>
            <p><strong><span>X</span></strong><strong><span>正方向</span></strong></p>
            </td>
            <td vAlign=top width=107>
            <p><strong><span>Y</span></strong><strong><span>正方向</span></strong></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>MM_TEXT(</span><span>默认方式</span><span>)</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>左上</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>像素</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>右</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>下</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>MM_HIENGLISH</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>左上</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>0.001</span><span>英寸</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>右</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>上</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>MM_LOENGLISH</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>左上</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>0.01</span><span>英寸</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>右</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>上</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>MM_HIMETRIC</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>左上</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>0.01</span><span>毫米</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>右</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>上</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>MM_LOMETRIC</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>左上</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>0.1</span><span>毫米</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>右</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>上</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>MM_TWIPS</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>左上</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>1/1440</span><span>英寸</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>右</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>上</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>MM_ISOTROPIC</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>可变</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>可变（</span><span>x=y</span><span>）</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>可变</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>可变</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>MM_ANISOTROPIC</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>可变</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>可变（</span><span>x!=y</span><span>）</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>可变</span></p>
            </td>
            <td vAlign=top width=107>
            <p><span>可变</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span>可以看到，除了默认的</span><span>MM_TEXT</span><span>方式外，下面</span><span>5</span><span>种映射方式：</span><span>MM_HIENGLISH</span><span>，</span><span>MM_LOENGLISH</span><span>，</span><span>MM_HIMETRIC</span><span>，</span><span>MM_LOMETRIC</span><span>和</span><span>MM_TWIPS</span><span>采用的都是原点位于左上角、</span><span>X</span><span>正方向向上的映射方式，另外，它们的坐标逻辑单位是不同的。</span></p>
<p>&nbsp;</p>
<p><span>最后的两种映射方式</span><span>MM_ISOTROPIC</span><span>和</span><span>MM_ANISOTROPIC</span><span>提供了更灵活的选择，设置为这两种映射方式后，程序可以继续调用</span><span>SetViewportOrgEx</span><span>，</span><span>SetViewportExtEx</span><span>和</span><span>SetWindowExtEx</span><span>函数来自由设置坐标系的原点、逻辑单位和坐标的正方向等所有参数。在其他映射方式下的时候，不能使用这</span><span>3</span><span>个设置函数，这时任何对它们的调用都会被忽略。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/127502.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-24 13:51 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/24/127502.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--定时器</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/23/127407.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Thu, 23 Sep 2010 03:40:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/23/127407.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/127407.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/23/127407.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/127407.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/127407.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>Win32</span><span>汇编</span><span>--</span><span>定时器</span></p>
<p>&nbsp;</p>
<p><span>1</span><span>、定时器简介</span></p>
<p><span>在应用程序需要使用定时器时，可以用</span><span>SetTimer</span><span>函数向</span><span>Windows</span><span>申请一个定时器，要求系统在指定的时间以后&#8220;通知&#8221;应用程序，如果申请成功的话，系统会以指定的时间周期调用</span><span>SetTimer</span><span>函数指定的回调函数，或者向指定的窗口过程发送</span><span>WM_TIMER</span><span>消息，和</span><span>DOS</span><span>操作系统固定以</span><span>55ms</span><span>的间隔触发中断服务程序相比，</span><span>SetTimer</span><span>函数可以指定的时间间隔更为灵活——以</span><span>ms</span><span>为单位，可以指定的时间周期为一个</span><span>32</span><span>位的整数，也就是从</span><span>1~4294 967 295ms</span><span>，这可是一个将近</span><span>50</span><span>天的范围！</span></p>
<p>&nbsp;</p>
<p><span>但是在具体的使用中不要被这个参数所迷惑：由于</span><span>Windows</span><span>的定时器同样是基于时钟中断的，所以虽然参数的单位是</span><span>ms</span><span>，但精度还是</span><span>55ms</span><span>，如果指定一个小于</span><span>55ms</span><span>的周期，不管是</span><span>1ms</span><span>还是</span><span>54ms</span><span>，</span><span>Windows</span><span>最快也只能在每个时钟中断的时候触发这个定时器，也就是说，实际上这个定时器是以</span><span>55ms</span><span>为触发周期的；另外，当指定一个时间间隔的时候，</span><span>Windows</span><span>以和这个间隔最接近的</span><span>55ms</span><span>的整数位时间来触发定时器，假定建立一个周期为</span><span>1000ms</span><span>的定时器，定时器的触发周期实际上不是</span><span>1s</span><span>而是</span><span>989ms</span><span>（</span><span>55ms*18</span><span>）。</span></p>
<p>&nbsp;</p>
<p><span>使用定时器时还有一个要点就是定时器消息是一个低级别的消息，这表现在两个方面：首先就是</span><span>Windows</span><span>只有在消息队列中没有其他消息的情况下才会发送</span><span>WM_TIMER</span><span>消息，如果窗口过程忙于处理某个消息没有返回，使消息队列中有消息积累起来，那么</span><span>WM_TIMER</span><span>消息就会被丢弃，在消息队列再度空闲的时候，被丢弃的</span><span>WM_TIMER</span><span>消息不会被补发；其次，消息队列中不会有多条</span><span>WM_TIMER</span><span>消息，如果消息队列中已经有一条</span><span>WM_TIMER</span><span>消息，还没来得及处理，又到了定时的时刻，那么两条</span><span>WM_TIMER</span><span>消息会被合并成一条。</span></p>
<p>&nbsp;</p>
<p><span>所以，应用程序不能依靠定时器来保证某件事情必须在规定的时刻被处理，另外，也不能依赖对定时器消息计数来确定已经过去了多少时间。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>2</span><span>、定时器的使用</span></p>
<p><span>//Timer.rc</span></p>
<p><span>#include &lt;resource.h&gt;</span></p>
<p><span>#define DLG_MAIN 1</span></p>
<p><span>#define ICO_1 1</span></p>
<p><span>#define ICO_2 2</span></p>
<p><span>#define IDC_SETICON 100</span></p>
<p><span>#define IDC_COUNT 101</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>ICO_1&nbsp;ICON "1.ico"</span></p>
<p><span>ICO_2&nbsp;ICON "2.ico"</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>DLG_MAIN&nbsp;DIALOG 50, 50, 113, 40</span></p>
<p><span>STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU</span></p>
<p><span>CAPTION "</span><span>定时器例子</span><span>"</span></p>
<p><span>FONT 9 "</span><span>宋体</span><span>"</span></p>
<p><span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>ICON ICO_1, IDC_SETICON, 8, 9, 18, 21</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>LTEXT "</span><span>计数：</span><span>", -1, 35, 16, 25, 10</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>LTEXT "", IDC_COUNT, 62, 16, 40, 10</span></p>
<p><span>}</span></p>
<p>&nbsp;</p>
<p><span>对资源的定义读者现在一定不会陌生了，这个文件中定义了两个图标和一个对话框，对话框中定义了一个图标框和两个文本框，其中的一个文本框中的文字为空，这是以后显示每秒一次的计数值用的。</span></p>
<p>&nbsp;</p>
<p><span>//Timer.asm</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.386</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.model flat, stdcall</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>option casemap:none</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; Include </span><span>文件定义</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>windows.inc</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.lib</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.lib</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>ID_TIMER1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span></p>
<p><span>ID_TIMER2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span></p>
<p><span>ICO_1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span></p>
<p><span>ICO_2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span></p>
<p><span>DLG_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span></p>
<p><span>IDC_SETICON<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>100</span></p>
<p><span>IDC_COUNT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>101</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>数据段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.data?</span></p>
<p><span>hInstance<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>hWinMain<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>dwCount<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>idTimer<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>代码段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.code</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>定时器过程</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>_ProcTimer<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp; </span>_hWnd, uMsg, _idEvent, _dwTime</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pushad</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke GetDlgItemInt, hWinMain, IDC_COUNT, NULL, FALSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc<span>&nbsp;&nbsp;&nbsp; </span>eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>invoke SetDlgItemInt, hWinMain, IDC_COUNT, eax, FALSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>popad</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>_ProcTimer<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>窗口过程</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>_ProcDlgMain<span>&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp; </span>uses ebx edi esi, hWnd, uMsg, wParam, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov eax, uMsg</span></p>
<p><span>;**********************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_TIMER</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, wParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == ID_TIMER1</span></p>
<p><span><span>&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; </span>inc<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>dwCount</span></p>
<p><span><span>&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; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, dwCount</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&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;</span>and<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, 1</span></p>
<p><span><span>&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; </span>inc<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax</span></p>
<p><span><span>&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; </span>invoke LoadIcon, hInstance, eax</span></p>
<p><span><span>&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; </span>invoke SendDlgItemMessage, hWnd, IDC_SETICON, STM_SETIMAGE, IMAGE_ICON, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif eax == ID_TIMER2</span></p>
<p><span><span>&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; </span>invoke MessageBeep, -1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span>;**********************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif eax == WM_INITDIALOG</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push<span>&nbsp;&nbsp;&nbsp; </span>hWnd</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>hWinMain</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SetTimer, hWnd, ID_TIMER1, 250, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SetTimer, hWnd, ID_TIMER2, 2000, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SetTimer, NULL, NULL, 1000, addr _ProcTimer</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>idTimer, eax</span></p>
<p><span>;**********************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif eax == WM_CLOSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke KillTimer, hWnd, ID_TIMER1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke KillTimer, hWnd, ID_TIMER2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke KillTimer, NULL, idTimer</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke EndDialog, hWnd, NULL</span></p>
<p><span>;**********************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.else</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, FALSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, TRUE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>_ProcDlgMain<span>&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>start:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke GetModuleHandle, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov hInstance, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke DialogBoxParam, hInstance, DLG_MAIN, NULL, offset _ProcDlgMain, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke ExitProcess, NULL</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>end start</span></p>
<p>&nbsp;</p>
<p><span>这个程序的基本结构非常简单，就是一个标准的对话框程序而已，在</span><span>WM_INITDIALOG</span><span>中用</span><span>SetTimer</span><span>申请了</span><span>3</span><span>个定时器，并在</span><span>WM_CLOSE</span><span>消息中用</span><span>KillTimer</span><span>撤销这</span><span>3</span><span>个定时器。</span></p>
<p>&nbsp;</p>
<p><span>申请一个定时器使用</span><span>SetTimer</span><span>函数，函数的使用方法如下：</span></p>
<p><span>invoke SetTimer, hWnd, nIDEvent, uElapse, lpTimerFunc</span></p>
<p><span>hWnd</span><span>参数是</span><span>WM_TIMER</span><span>消息发往的窗口句柄；</span><span>nIDEvent</span><span>参数是一个用户指定的任意整数，用来标识一个程序中的多个定时器；</span><span>uElapse</span><span>是时间周期，以</span><span>ms</span><span>为单位，这个参数是必须指定的；</span><span>lpTimerFunc</span><span>是定时器过程，在下面的内容中有详细介绍。如果定时器建立成功的话，函数的返回值是定时器的标识符。</span></p>
<p>&nbsp;</p>
<p><span>撤销定时器的函数是</span><span>KillTimer</span><span>，该函数的使用方法是：</span></p>
<p><span>invoke KillTimer, hWnd, uIDEvent</span></p>
<p><span>参数</span><span>hWnd</span><span>和</span><span>uIDEvent</span><span>就是建立定时器时使用的数值。</span></p>
<p><span>使用</span><span>SetTimer</span><span>函数的方法有两种，第一种方法是要求</span><span>Windows</span><span>将</span><span>WM_TIMER</span><span>消息发往指定的窗口过程，这时候</span><span>lpTimerFunc</span><span>必须为</span><span>NULL</span><span>，如例子中的：</span></p>
<p><span>invoke SetTimer, hWnd, ID_TIMER1, 250, NULL</span></p>
<p><span>invoke SetTimer, hWnd, ID_TIMER2, 2000, NULL</span></p>
<p><span>这两个句子设置了两个标识分别为</span><span>ID_TIMER1</span><span>和</span><span>ID_TIMER2</span><span>的定时器，定时周期分别为</span><span>250ms</span><span>和</span><span>2s</span><span>。在窗口过程收到</span><span>WM_TIMER</span><span>消息的时候，</span><span>wParam</span><span>中是用</span><span>SetTimer</span><span>建立定时器时使用的标识</span><span>uIDEvent</span><span>，所以程序可以建立一个分支，通过判断</span><span>wParam</span><span>来处理不同的定时器引起的</span><span>WM_TIMER</span><span>的消息。在例子中，当</span><span>wParam</span><span>是</span><span>ID_TIMER1</span><span>的时候更换图标框中的图标，是</span><span>ID_TIMER2</span><span>的时候用</span><span>MessageBeep</span><span>函数来发出一声&#8220;嘟&#8221;的声音。如果要撤销用这种方法建立的定时器，那么只需要用建立时的</span><span>hWnd</span><span>和</span><span>uIDEvent</span><span>参数简单地调用</span><span>KillTimer</span><span>就可以了。</span></p>
<p>&nbsp;</p>
<p><span>还有一种使用定时器的方法，那就是要求</span><span>Windows</span><span>在时间到的时候调用指定的定时器过程，而不是某个窗口过程，那么只需要指定</span><span>lpTimerFunc</span><span>参数，如例子中的：</span></p>
<p><span>invoke SetTimer, NULL, NULL, 1000, addr _ProcTimer</span></p>
<p><span>这句语句要求系统把定时器消息发送到</span><span>_ProcTimer</span><span>定时器过程中去，但是，这时候没有参数用来指定定时器标识，到最后如何用</span><span>KillTimer</span><span>撤销这个定时器呢？答案是</span><span>SetTimer</span><span>函数会返回一个标识，程序可以保存这个标识并在</span><span>KillTimer</span><span>函数中使用。</span></p>
<p>&nbsp;</p>
<p><span>当然，这种用法中的定时器标识也可以自己指定，但这时候一定要同时指定</span><span>hWnd</span><span>，虽然这个</span><span>hWnd</span><span>没有实际的用途，如果</span><span>hWnd</span><span>为</span><span>NULL</span><span>，那么即使指定了定时器标识，这个标识也会被忽略，如：</span></p>
<p><span>invoke SetTimer, hWnd, ID_TIMER3, 1000, addr _ProcTimer</span></p>
<p><span>这个语句定义了一个标识为</span><span>ID_TIMER3</span><span>、消息发往</span><span>_ProcTimer</span><span>子程序的定时器。</span></p>
<p>&nbsp;</p>
<p><span>定时器过程是如下定义的：</span></p>
<p><span>TimerProc<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hWnd, uMsg, idEvent, dwTime</span></p>
<p><span>Windows</span><span>回调定时器过程的时候会有</span><span>4</span><span>个参数，</span><span>uMsg</span><span>总是</span><span>WM_TIMER</span><span>，</span><span>hWnd</span><span>和</span><span>idEvent</span><span>是窗口句柄和定时器标识。由于有</span><span>idEvent</span><span>参数，所以我们同样可以把多个定时器消息指向同一个定时器过程中，并且根据</span><span>idEvent</span><span>参数构建一个分支来处理不同定时器引发的消息。</span></p>
<p>&nbsp;</p>
<p><span>程序中还可能遇到一种情况：当在</span><span>SetTimer</span><span>中指定的定时器标识已经存在会怎样呢？答案是</span><span>Windows</span><span>会用新的参数代替老的定时器参数，函数执行以后，这个标识的定时器消息将以新的时间周期发送。</span></p>
<p>&nbsp;</p>
<p><span>注意：例子程序的窗口过程中把</span><span>WM_TIMER</span><span>的消息的处理代码放在第一个分支上，这是对程序的简单优化，把频繁发生的消息放到前面可以使程序少执行一系列的比较指令，像</span><span>WM_CREATE</span><span>和</span><span>WM_DESTROY</span><span>等仅发生一次的消息可以放到分支的最后面。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>3</span><span>、取</span><span>Windows</span><span>时间</span></p>
<p><span>&#8220;定时器&#8221;这个词很容易让人联想到时钟，但是在前面介绍过，定时器是不能用来构造时钟的，定时器用于时钟程序中只能是用在定时刷新屏幕这个功能上，要得到系统的时间还是要靠别的方法。</span></p>
<p>&nbsp;</p>
<p><span>在</span><span>Win32</span><span>编程中，和获得系统时间相关的函数有</span><span>3</span><span>个：</span></p>
<p><span>invoke GetLocalTime, lpSystemTime</span></p>
<p><span>invoke GetSystemTime, lpSystemTime</span></p>
<p><span>invoke GetTickCount</span></p>
<p>&nbsp;</p>
<p><span>它们之间的区别是：</span></p>
<p><span>GetTickCout</span><span>返回的是本次</span><span>Windows</span><span>启动以来的</span><span>ms</span><span>数，得到的时间数值直接在</span><span>eax</span><span>中返回，由于这是一个</span><span>32</span><span>位的整数，可以表示的范围是</span><span>1~ffffffffms</span><span>，所以当</span><span>Windows</span><span>连续运行</span><span>49.7</span><span>天以后，计数器会清零并重新开始。</span></p>
<p>&nbsp;</p>
<p><span>GetLocalTime</span><span>返回当前的时间，</span><span>GetSystemTime</span><span>返回当前的格林威治标准时间，这两个函数返回的时间数据包括年、月、日、时、分、秒、毫秒以及星期，数据比较多，所以无法放在</span><span>eax</span><span>中返回，应用程序需要预先设置一个</span><span>SYSTEMTIME</span><span>结构的缓冲区，并将缓冲区地址</span><span>lpSystemTime</span><span>当参数传递给函数，函数会把时间数据返回到这个缓冲区中。</span></p>
<p>&nbsp;</p>
<p><span>SYSTEMTIME</span><span>结构的定义如下：</span></p>
<p><span>SYSTEMTIME STRUCT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wYear<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>年</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wMonth<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>月</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wDayOfWeek<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>星期，</span><span>0=</span><span>星期日，</span><span>1=</span><span>星期一，&#8230;&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wDay<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>日</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wHour<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>时</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wMinute<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>分</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wSecond<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>秒</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wMilliseconds<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WORD<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>毫秒</span></p>
<p><span>SYSTEMTIME ENDS</span></p>
<p><span>需要注意的是，结构中的字段全部是</span><span>word</span><span>类型的，而</span><span>Win32</span><span>程序中用的往往是</span><span>dword</span><span>型变量，所以在使用这些数据之前往往要先把它们转换为</span><span>dword</span><span>类型，用</span><span>movzx</span><span>指令就可以很方便地完成这个工作，如</span><span>movzx eax, stSystemTime.wYear</span><span>将</span><span>wYear</span><span>字段扩展到</span><span>32</span><span>位后放在</span><span>eax</span><span>中。</span></p>
<p>&nbsp;</p>
<p><span>和获得系统时间的函数相对应，可以用下面的两个函数设置系统时间：</span></p>
<p><span>invoke SetLocalTime, lpSystemTime</span></p>
<p><span>invoke SetSystemTime, lpSystemTime</span></p>
<p><span>同样，</span><span>SetLocalTime</span><span>中的参数代表本地时间，</span><span>SetSysTime</span><span>中的参数代表格林威治标准时间，在调用函数之前，要把需要设置的时间放到一个</span><span>SYSTEMTIME</span><span>结构中并把结构地址当做参数传递给</span><span>Windows</span><span>。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/127407.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-23 11:40 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/23/127407.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--二进制资源与自定义资源</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/21/127258.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Tue, 21 Sep 2010 08:16:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/21/127258.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/127258.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/21/127258.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/127258.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/127258.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>Win32</span><span>汇编</span><span>--</span><span>使用资源</span><span>--</span><span>二进制资源与自定义资源</span></p>
<p>&nbsp;</p>
<p><span>1</span><span>、使用二进制资源</span></p>
<p><span>DOS</span><span>的</span><span>exe</span><span>文件可以带一个覆盖部分，覆盖部分实际上就是在真正的可执行部分后面附加的数据，然后由程序在运行中打开自身文件并使用这些数据。</span><span>Win32</span><span>的可执行文件中除了上面介绍的这些标准类型的资源外，也可以在程序中附带其他数据，当然方法完全不同——</span><span>Win32</span><span>资源中允许用户自己定义二进制的资源或者自定义格式的资源，资源的内容可以是任何数据，也可以将一个磁盘文件按二进制格式包括进去。</span></p>
<p>&nbsp;</p>
<p><span>二进制资源的定义格式是：</span></p>
<p><span>资源</span><span>ID REDATA [DISCARDABLE]</span></p>
<p><span>BEGIN</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>数据定义</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;</span></p>
<p><span>END</span></p>
<p>&nbsp;</p>
<p><span>也可以用一个磁盘文件当做资源的内容：</span></p>
<p><span>资源</span><span>ID RCDATA [DISCARDABLE] </span><span>文件名</span></p>
<p>&nbsp;</p>
<p><span>在程序中要使用资源的内容时，可以通过以下步骤将资源装入内存使用：</span></p>
<p><span>（</span><span>1</span><span>）用</span><span>FindResource(hInstance, lpName, lpType)</span><span>查找资源。</span><span>lpName</span><span>的值为资源</span><span>ID</span><span>，</span><span>lpType</span><span>的值为</span><span>RT_RCDATA</span><span>，如果找到资源。那么函数返回一个资源信息句柄。</span></p>
<p><span>（</span><span>2</span><span>）用</span><span>LoadResource(hInstance, hResInfo)</span><span>装入资源。</span><span>hResInfo</span><span>是上一步中得到的资源信息句柄，装入成功的话函数会返回一个资源句柄。</span></p>
<p><span>（</span><span>3</span><span>）用</span><span>LckResource(hResData)</span><span>将资源锁定到内存中。</span><span>hResData</span><span>是上一步得到的资源句柄，函数返回资源装入的内存地址，程序就可以使用内存中的数据了。</span></p>
<p><span>（</span><span>4</span><span>）如果想知道装入资源的大小是多少，可以使用</span><span>FindResource</span><span>返回的</span><span>hResInfo</span><span>来调用</span><span>SizeofResource(hInstance, hResInfo)</span><span>从而得到资源大小。</span></p>
<p>&nbsp;</p>
<p><span>下面是一个装入资源</span><span>ID</span><span>为</span><span>ID_MYRES</span><span>的</span><span>RCDATA</span><span>类型资源的例子：</span></p>
<p><span>invoke FindResource, hInstance, ID_MYRES, RT_RCDATA </span><span>；寻找资源</span></p>
<p><span>.if&nbsp;eax</span></p>
<p><span>mov&nbsp;hResInfo, eax</span></p>
<p><span>invoke SizeofResource, hInstance, eax<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>获取资源尺寸</span></p>
<p><span>mov dwResSize, eax</span></p>
<p><span>invoke LoadResource, hInstance, hResInfo&nbsp;;</span><span>装入资源</span></p>
<p><span>.if&nbsp;eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>invoke LockResource, eax<span>&nbsp;&nbsp; </span>;</span><span>锁定资源</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp; </span>.if&nbsp;eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov&nbsp;lpRes, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>处理</span><span>lpRes</span><span>指向的资源内容</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span>.endif</span></p>
<p><span>.endif</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、使用自定义资源</span></p>
<p><span>自定义资源的定义格式比二进制资源更灵活，它和二进制资源的区别在于可以指定资源类别为自定义的名称：</span></p>
<p><span>资源</span><span>ID&nbsp;</span><span>类型</span><span>ID [DISCARDABLE]</span></p>
<p><span>BEGIN</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>数据定义</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;</span></p>
<p><span>END</span></p>
<p>&nbsp;</p>
<p><span>或用一个磁盘文件当做资源的内容：</span></p>
<p><span>资源</span><span>ID&nbsp;</span><span>类型</span><span>ID&nbsp;[DISCARDABLE] </span><span>文件名</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>类型</span><span>ID</span><span>可以是大于</span><span>255</span><span>的数值（</span><span>255</span><span>及以下的数值由</span><span>Windows</span><span>使用）或字符串，如可以定义如下：</span></p>
<p><span>1000<span>&nbsp;&nbsp; </span>WAVE<span>&nbsp;&nbsp; </span>&#8220;Hello.wav&#8221;<span>&nbsp;&nbsp; </span>;</span><span>定义类型为&#8220;</span><span>WAVE</span><span>&#8221;，资源</span><span>ID</span><span>为</span><span>1000</span><span>的资源</span></p>
<p><span>1000<span>&nbsp;&nbsp; </span>TEXT<span>&nbsp;&nbsp;&nbsp; </span>&#8220;Readme.txt&#8221;&nbsp;;</span><span>定义类型为&#8220;</span><span>TEXT</span><span>&#8221;，资源</span><span>ID</span><span>为</span><span>1000</span><span>的资源</span></p>
<p><span>1000<span>&nbsp;&nbsp; </span>1000<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8220;Test.bin&#8221;<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>定义类型为</span><span>1000</span><span>，资源</span><span>ID</span><span>为</span><span>1000</span><span>的资源</span></p>
<p>&nbsp;</p>
<p><span>在程序中使用自定义资源的方法和使用二进制资源类似，唯一的区别是使用</span><span>FindResource</span><span>得到</span><span>hResInfo</span><span>的参数有些区别，得到</span><span>hResInfo</span><span>以后的步骤是一模一样的。针对上面</span><span>3</span><span>句定义，查找资源的方法可以是：</span></p>
<p><span>szResType1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8220;WAVE&#8221;,0</span></p>
<p><span>szResType2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8220;TEXT&#8221;,0</span></p>
<p><span>&#8230;</span></p>
<p><span>invoke FindResource, hInstance, 1000, addr szResType1&nbsp;;</span><span>针对上面第一句</span></p>
<p><span>invoke FindResource, hInstance, 1000, addr szResType2&nbsp;;</span><span>针对上面第二句</span></p>
<p><span>invoke FindResource, hInstance, 1000, 1000<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>针对上面第三句</span></p>
<p>&nbsp;</p>
<p><span>在使用完二进制或自定义资源以后，不必使用任何函数去释放它们，</span><span>Windows</span><span>在程序退出的时候会自动将它们释放。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/127258.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-21 16:16 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/21/127258.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--版本信息资源</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/21/127239.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Tue, 21 Sep 2010 05:24:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/21/127239.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/127239.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/21/127239.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/127239.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/127239.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;Win32汇编--使用资源—版本信息资源&nbsp;有时应用程序需要确保自己运行时使用某一特定版本的DLL，以便确保可以使用某些函数。检测版本是通过API函数查询定义于资源中的版本信息来完成的，如果资源中没有定义版本，那么就无法知道一个文件的版本究竟是多少。&nbsp;版本信息是以VERSIONINFO类型的资源保存在应用程序中的，里面可以定义的信息包括文件的版本号...&nbsp;&nbsp;<a href='http://www.cppblog.com/luqingfei/archive/2010/09/21/127239.html'>阅读全文</a><img src ="http://www.cppblog.com/luqingfei/aggbug/127239.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-21 13:24 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/21/127239.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--字符串资源</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/20/127117.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Mon, 20 Sep 2010 02:12:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/20/127117.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/127117.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/20/127117.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/127117.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/127117.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>Win32</span><span>汇编</span><span>--</span><span>使用资源</span><span>--</span><span>字符串资源</span></p>
<p>&nbsp;</p>
<p><span>程序中用到的字符串常常定义在</span><span>.const</span><span>字段中，但</span><span>Windows</span><span>也提供了另外一种使用字符串常量的方法，那就是在资源中定义。虽然在资源中定义字符串使用起来比直接在</span><span>.const</span><span>段中定义要复杂一点，但它带来的好处是便于开发不同语言的版本，比如，要推出其他语种的版本只需要修改资源中的字符串表就可以了，即使语言转换的工作是由第三方通过修改可执行文件来做的（如编程受好者常常做的汉化工作），修改资源也远比修改代码来得快捷和安全。</span></p>
<p>&nbsp;</p>
<p><span>在资源脚本中定义字符串的语法是：</span></p>
<p><span>STRINGTABLE&nbsp;[DISCARDABLE]</span></p>
<p><span>BEGIN</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>字符串</span><span>ID1<span>&nbsp;&nbsp;&nbsp; </span>&#8220;</span><span>字符串</span><st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="&#8221;"><span>1&#8221;</span></st1:chmetcnv></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>字符串</span><span>ID2<span>&nbsp;&nbsp;&nbsp; </span>&#8220;</span><span>字符串</span><st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="2" UnitName="&#8221;"><span>2&#8221;</span></st1:chmetcnv></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;</span></p>
<p><span>END</span></p>
<p>&nbsp;</p>
<p><span>全部字符串组成一个字符串表，和其他资源定义不同，由于整个资源文件中只能定义一个字符串表，所以字符串表没有资源</span><span>ID</span><span>，但是表中的不同字符串分别有一个字符串</span><span>ID</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>在程序中使用字符串资源也很简单，用</span><span>LoadString</span><span>把字符串装入到缓冲区中去就可以用了：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke LoadString, hInstance, </span><span>字符串</span><span>ID, addr </span><span>缓冲区</span><span>, sizeof </span><span>缓冲区</span></p>
<p>&nbsp;</p>
<p><span>为了防止溢出，最后一个参数指定缓冲区的长度。</span></p>
<p><span>如果要在单个可执行文件中实现多语种，那么可以在字符串表中定义不同语言的字符串。同一语种的字符串按规律排列，如下列中文的以</span><span>1000</span><span>开头，英文的以</span><span>2000</span><span>开头：</span></p>
<p><span>stringtable</span></p>
<p><span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1001<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8220;</span><span>文件未找到！</span><span>&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1002<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8220;</span><span>无法打开文件！</span><span>&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2001<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8220;File not found!&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2002<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8220;Can not open file&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;</span></p>
<p><span>}</span></p>
<p>&nbsp;</p>
<p><span>在程序中使用的时候，先确定一种语言并预先设置在</span><span>dwLanguage</span><span>变量中，使用中文时将</span><span>dwLanguage</span><span>设置为</span><span>1000</span><span>，使用英文时设置为</span><span>2000</span><span>，再写一个读取不同版本字符串的子程序</span><span>_GetString</span><span>，这样调用</span><span>_GetString</span><span>子程序后就不用考虑版本问题了：</span></p>
<p><span>_GetString<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>_dwID, _lpBuffer, _dwSize</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pushad</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, _dwID</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>LoadString, hInstance, eax, _lpBuffer, _dwSize</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>popad</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>_GetString<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/127117.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-20 10:12 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/20/127117.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--对话框--在对话框中使用子窗口控件(3)</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/20/127116.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Mon, 20 Sep 2010 01:45:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/20/127116.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/127116.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/20/127116.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/127116.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/127116.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>使用资源</span><span>--</span><span>对话框</span><span>--</span><span>在对话框中使用子窗口控件</span></p>
<p>&nbsp;</p>
<p><span>8</span><span>、使用列表框</span></p>
<p><span>列表框提供一个可供用户选择的列表，用户可以一次选择一个项目，也可以同时选中多个项目。</span></p>
<p><span>//Listbox.rc</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>#include &lt;resource.h&gt;</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>#define ICO_MAIN&nbsp;0x1000&nbsp;//</span><span>图标</span></p>
<p><span>#define DLG_MAIN&nbsp;1</span></p>
<p><span>#define IDC_LISTBOX1 101</span></p>
<p><span>#define IDC_LISTBOX2 102</span></p>
<p><span>#define IDC_SEL1 103</span></p>
<p><span>#define IDC_RESET 104</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>ICO_MAIN<span>&nbsp;&nbsp; </span>ICON<span>&nbsp;&nbsp;&nbsp; </span>"Main.ico"</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>DLG_MAIN DIALOG 163, 160, 190, 108</span></p>
<p><span>STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU</span></p>
<p><span>CAPTION "</span><span>列表框控件示例</span><span>"</span></p>
<p><span>FONT 9, "</span><span>宋体</span><span>"</span></p>
<p><span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>LISTBOX IDC_LISTBOX1, 6, 5, 55, 86, LBS_STANDARD</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>LISTBOX IDC_LISTBOX2, 68, 5, 115, 86, LBS_STANDARD | LBS_MULTIPLESEL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>LTEXT "", IDC_SEL1, 6, 93, 55, 8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>PUSHBUTTON "</span><span>复位</span><span>(&amp;R)", IDC_RESET, 89, 90, 45, 14</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>DEFPUSHBUTTON "</span><span>查看</span><span>(&amp;S)", IDOK, 139, 90, 45, 14, WS_DISABLED</span></p>
<p><span>}</span></p>
<p>&nbsp;</p>
<p><span>//Listbox.asm</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.386</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>.model flat, stdcall</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>option casemap :none</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; Include </span><span>文件定义</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>windows.inc</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.lib</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.lib</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; Equ</span><span>等值定义</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>ICO_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>1000h</span></p>
<p><span>DLG_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span></p>
<p><span>IDC_LISTBOX1<span>&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>101</span></p>
<p><span>IDC_LISTBOX2<span>&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>102</span></p>
<p><span>IDC_SEL1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>103</span></p>
<p><span>IDC_RESET<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>104</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>数据段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.data?</span></p>
<p><span>hInstance<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.const</span></p>
<p><span>szText1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'</span><span>项目</span><span>1',0</span></p>
<p><span>szText2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'</span><span>项目</span><span>2',0</span></p>
<p><span>szText3<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'</span><span>项目</span><span>3',0</span></p>
<p><span>szPath<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'*.*',0</span></p>
<p><span>szMessage<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'</span><span>选择结果：</span><span>%s',0</span></p>
<p><span>szTitle<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'</span><span>您的选择</span><span>',0</span></p>
<p><span>szSelect<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'</span><span>您选择了以下的项目：</span><span>'</span></p>
<p><span>szReturn<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>0dh,0ah,0</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>代码段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.code</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>_ProcDlgMain<span>&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp; </span>uses ebx edi esi hWnd, wMsg, wParam, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@szBuffer[128]:byte</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;</span>local<span>&nbsp;&nbsp; </span>@szBuffer1[128]:byte</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@szTextBuff[2048]:byte</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@dwCount</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, wMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_CLOSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke EndDialog, hWnd, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif eax == WM_INITDIALOG</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke LoadIcon, hInstance, ICO_MAIN</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendMessage, hWnd, WM_SETICON, ICON_BIG, eax</span></p>
<p><span>;********************************************************************************</span></p>
<p><span>; </span><span>初始化列表框</span></p>
<p><span>;********************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hWnd, IDC_LISTBOX1, LB_ADDSTRING, 0, addr szText1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hWnd, IDC_LISTBOX1, LB_ADDSTRING, 0, addr szText2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hWnd, IDC_LISTBOX1, LB_ADDSTRING, 0, addr szText3</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hWnd, IDC_LISTBOX2, LB_DIR, DDL_ARCHIVE or DDL_DRIVES or DDL_DIRECTORY, addr szPath</span></p>
<p><span>;********************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif eax == WM_COMMAND</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, wParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ax == IDOK</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&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;</span>invoke SendDlgItemMessage, hWnd, IDC_LISTBOX2, LB_GETSELCOUNT, 0, 0</span></p>
<p><span><span>&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; </span>mov @dwCount, eax</span></p>
<p><span><span>&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; </span>invoke SendDlgItemMessage, hWnd, IDC_LISTBOX2, LB_GETSELITEMS, 128/4, addr @szBuffer</span></p>
<p><span><span>&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; </span>invoke lstrcpy, addr @szTextBuff, addr szSelect</span></p>
<p><span><span>&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; </span>lea<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>esi, @szBuffer</span></p>
<p><span><span>&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; </span>.while&nbsp;@dwCount</span></p>
<p><span><span>&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; </span>lodsd</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>lea<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ecx, @szBuffer1</span></p>
<p><span><span>&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; </span>invoke&nbsp;SendDlgItemMessage, hWnd, IDC_LISTBOX2, LB_GETTEXT, eax, ecx</span></p>
<p><span><span>&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; </span>invoke&nbsp;lstrcat, addr @szTextBuff, addr szReturn</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&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;</span>invoke&nbsp;lstrcat, addr @szTextBuff, addr @szBuffer1</span></p>
<p><span><span>&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; </span>dec<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@dwCount</span></p>
<p><span><span>&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; </span>.endw</span></p>
<p><span><span>&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; </span>invoke&nbsp;MessageBox, hWnd, addr @szTextBuff, addr szTitle, MB_OK</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif ax == IDC_RESET</span></p>
<p><span><span>&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; </span>invoke&nbsp;SendDlgItemMessage, hWnd, IDC_LISTBOX2, LB_SETSEL, FALSE, -1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif ax == IDC_LISTBOX1</span></p>
<p><span><span>&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; </span>shr<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, 16</span></p>
<p><span><span>&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; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ax == LBN_SELCHANGE</span></p>
<p><span>;********************************************************************************</span></p>
<p><span>; </span><span>将鼠标点击结果显示在文本框中</span></p>
<p><span>;********************************************************************************</span></p>
<p><span><span>&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; </span>invoke SendMessage, lParam, LB_GETCURSEL, 0, 0</span></p>
<p><span><span>&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; </span>lea<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ecx, @szBuffer</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&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;</span>invoke&nbsp;SendMessage, lParam, LB_GETTEXT, eax, ecx</span></p>
<p><span><span>&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; </span>invoke&nbsp;SetDlgItemText, hWnd, IDC_SEL1, addr @szBuffer</span></p>
<p><span>;********************************************************************************</span></p>
<p><span>; </span><span>双击项目则弹出对话框</span></p>
<p><span>;********************************************************************************</span></p>
<p><span><span>&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; </span>.elseif ax == LBN_DBLCLK</span></p>
<p><span><span>&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; </span>invoke&nbsp;SendMessage, lParam, LB_GETCURSEL, 0, 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>lea<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ecx, @szBuffer</span></p>
<p><span><span>&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; </span>invoke&nbsp;SendMessage, lParam, LB_GETTEXT, eax, ecx</span></p>
<p><span><span>&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; </span>invoke&nbsp;wsprintf, addr @szBuffer1, addr szMessage, addr @szBuffer</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&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;</span>invoke&nbsp;MessageBox, hWnd, addr @szBuffer1, addr szTitle, MB_OK</span></p>
<p><span><span>&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; </span>.endif</span></p>
<p><span>;********************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif ax == IDC_LISTBOX2</span></p>
<p><span><span>&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; </span>shr<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, 16</span></p>
<p><span><span>&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; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ax == LBN_SELCHANGE</span></p>
<p><span><span>&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; </span>invoke SendMessage, lParam, LB_GETSELCOUNT, 0, 0</span></p>
<p><span><span>&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; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ebx, eax</span></p>
<p><span><span>&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; </span>invoke&nbsp;GetDlgItem, hWnd, IDOK</span></p>
<p><span><span>&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; </span>invoke&nbsp;EnableWindow, eax, ebx</span></p>
<p><span><span>&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; </span>.endif</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.else</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, FALSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, TRUE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>_ProcDlgMain<span>&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>start:</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>invoke GetModuleHandle, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp; </span>hInstance, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke DialogBoxParam, hInstance, DLG_MAIN, NULL, offset _ProcDlgMain, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke ExitProcess, NULL</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>end start</span></p>
<p>&nbsp;</p>
<p><span>程序中总共定义了两个列表框。左边列表框为</span><span>IDC_LISTBOX1</span><span>，这是一个单选的列表框，选择一个项目的时候下面的文本会显示出选择的项目，双击某个项目的时候会弹出消息框，显示所选中的项目。右边的列表框是</span><span>IDC_LISTBOX2</span><span>，是一个多选的列表框，选择完毕可以用&#8220;查看&#8221;按钮弹出消息框，消息框中显示了所有选中的项目。按下&#8220;复位&#8221;按钮清除列表框的选择。</span></p>
<p align=center><span>列表框可以使用的风格</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=199>
            <p><span>风格</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>说明</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=199>
            <p><span>LBS_DISABLENOSCROLL</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>在不需滚动的时候也显示垂直滚动条</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=199>
            <p><span>LBS_EXTENDEDSEL</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>在多选列表框中允许按住</span><span>Shitf</span><span>键同时选中一个范围</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=199>
            <p><span>LBS_MULTIPLESEL</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>允许多选，如果不定义的话则是单选列表框</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=199>
            <p><span>LBS_NOSEL</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>列表框项目只能查看不能选择</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=199>
            <p><span>LBS_NOTIFY</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>用户点击或双击项目时向父窗口发送</span><span>WM_COMMAND</span><span>消息</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=199>
            <p><span>LBS_SORT</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>自动按字母顺序排序插入的项目</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=199>
            <p><span>LBS_USETABSTOPS</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>列表框项目的文本中允许将</span><span>Tab</span><span>字符的位置展开</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=199>
            <p><span>LBS_STANDARD</span></p>
            </td>
            <td vAlign=top width=360>
            <p><span>组合</span><span>LBS_NOTIFY, LBS_SORT, WS_VSCROLL</span><span>和</span><span>WS_BORDER</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span>一般单选列表框只需定义</span><span>LBS_STANDARD</span><span>就可以了。</span></p>
<p>&nbsp;</p>
<p><span>列表框使用说明：</span></p>
<p><span>当列表框有</span><span>&nbsp;LBS</span><span>——</span><span>NOTIFY</span><span>风格的时候，用户有所动作时列表框会向父窗口发送</span><span>WM_COMMAND</span><span>，同时在</span><span>wParam</span><span>的高</span><span>16</span><span>位中指定通知码，列表框的通知码种类很少，基本上就是以下几种：</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>LBN_DBLCLK<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>用户双击了一个项目。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>LBN_ERRSPACE<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>插入项目时无法申请到足够的内存。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>LBN_KILLFOCUS<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>输入焦点被切换到其他控件中，列表框丢失了焦点。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>LBN_SELCANCEL<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>用户撤销了一个选择。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>LBN_SELCHANGE<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>选定状态改变。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>LBN_SETFOCUS<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>列表框得到输入焦点。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>我们最关心的是</span><span>LBN_DBLCLK</span><span>和</span><span>LBN_SELCHANGE</span><span>通知码，在单选列表框中，如果程序用双击来选择项目，那么就要处理</span><span>LBN_DBLCLK</span><span>通知，例子程序中当用户双击</span><span>IDC_LISTBOX1</span><span>时弹出一个消息框，读者可以查看其使用方法。在多选列表框中，由于用户可能选择了多个项目，所以一般不用双击的方法选定：如果收到</span><span>LBN_SELCHANGE</span><span>通知的话，可以得知用户有一个选择动作，在这里可以进行相应的操作。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>列表框通知父窗口是通过发送</span><span>WM_COMMAND</span><span>消息，而程序控制列表框的时候是通过向列表框发送消息来完成的。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>常用的列表框消息如下表所示：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=127>
            <p><strong><span>消息</span></strong></p>
            </td>
            <td vAlign=top width=108>
            <p><strong><span>wParam</span></strong></p>
            </td>
            <td vAlign=top width=96>
            <p><strong><span>lParam</span></strong></p>
            </td>
            <td vAlign=top width=228>
            <p><strong><span>说明</span></strong></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_ADDSTRING</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>字符串地址</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>添加一个项目，返回加入后的索引。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_DELETESTRING</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>删除一个项目，返回剩余的项数。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_FINDSTRING</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>开始索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>字符串地址</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>查找以字符串开头的项目，找到则返回位置索引，未找到则返回</span><span>LB_ERR</span><span>。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_FINDSTRINGEXACT</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>开始索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>字符串地址</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>精确查找一个项目，返回值同上。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETANCHORINGEX</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>返回多选列表框多选时的起始位置。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETCARETINGEX</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>多选列表框中的当前焦点项目位置。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETCOUNT</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>返回列表框中的项目总数</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETCURSEL</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>返回单选列表框当前选中的项目。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETSEL</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>检测指定项目的选中状态，返回非</span><span>0</span><span>为选中，返回</span><span>0</span><span>为未选中。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETSELCOUNT</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>返回多选列表框选中项目的总数</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETSELITEMS</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>最大项数</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>缓冲区地址</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>返回多选列表框的选中项目索引列表到缓冲区中</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETTEXT</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>缓冲区地址</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>返回某个项目的字符串</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETTEXTLEN</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>返回某个项目的字符串长度</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_GETTOPINDEX</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>返回当前可见的第一个项目位置</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_INSERTSTRING</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>插入位置</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>字符串地址</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>在指定位置插入一个项目</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_RESETCONTENT</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>删除所有项目</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_SELECTSTRING</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>开始位置</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>字符串地址</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>将以指定字符串开头的项目选中</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_SELITEMRANGE</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>选择状态</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>范围</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>在多选框中将一个范围选中或清除</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_SETCURSEL</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>在单选列表框中选中一个项目</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_SETSEL</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>选择状态</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>在多选框中将一个项目选中或清除</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_SETTOPINDEX</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>滚动显示到指定的项目</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>LB_DIR</span></p>
            </td>
            <td vAlign=top width=108>
            <p><span>属性</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>文件通配符</span></p>
            </td>
            <td vAlign=top width=228>
            <p><span>搜索目录并将符合文件通配符的文件名加入到列表框中</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>这些消息中</span><span>LB_DIR</span><span>是个比较有趣的消息，它可以将指定目录中的文件名自动列出来并加入列表框中，如例子中用</span><span>*.*</span><span>将当前目录中的全部文件名加到列表框中。</span><span>LB_DIR</span><span>消息中</span><span>wParam</span><span>参数可以指定的属性可以是是以下值的组合：</span></p>
<p><span>DDL_ARCHIVE<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>加入归档属性的文件。</span></p>
<p><span>DDL_DIRECTORY<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>加入目录。</span></p>
<p><span>DDL_DRIVES<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>加入驱动器名。</span></p>
<p><span>DDL_HIDDEN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>包含隐含文件。</span></p>
<p><span>DDL_READONLY<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>包含只读文件。</span></p>
<p><span>DDL_READWRITE<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>包含可读写的文件。</span></p>
<p><span>DDL_SYSTEM<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>包含系统文件。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在列表框中初始化时加入项目可以使用</span><span>LB_ADDSTRING</span><span>和</span><span>LB_INSERTSTRING</span><span>消息，删除项目可以用</span><span>LB_DELETESTRING</span><span>消息，删除全部项目用</span><span>LB_RESETCONTENT</span><span>消息。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于单选列表框，要获取选中项目可以发送</span><span>LB_GETCURSEL</span><span>消息，要得到这个项目的字符串需要用索引值通过</span><span>LB_GETTEXT</span><span>消息获取，读者可以查看例子中处理</span><span>LBN_DBLCLK</span><span>通知码的部分代码。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于多选列表框，需要用</span><span>LB_GETSELITEMS</span><span>消息获取全部选中项目，这个消息返回的是一个列表，所有选中项目的索引按顺序排列返回到缓冲区中，所以在例子中处理&#8220;查看&#8221;按钮消息（</span><span>IDOK</span><span>）的时候，程序先发送</span><span>LB_GETSELCOUNT</span><span>消息得到选中的项目数，以便在下面用一个循环获取所有的项目，得到数目数后，再用</span><span>LB_GETSELITEMS</span><span>将选中项目的索引取到</span><span>@szBuffer</span><span>中，接下来进入一个循环，循环的次数就是</span><span>LB_GETSELCOUNT</span><span>得到的数值，在循环中，程序从</span><span>@szBuffer</span><span>中将索引值逐个取出并用</span><span>LB_GETTEXT</span><span>消息获取每一项的字符串，最后用一个</span><span>MessageBox</span><span>显示出来。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/127116.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-20 09:45 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/20/127116.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--对话框--在对话框中使用子窗口控件(2)</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/16/126773.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Thu, 16 Sep 2010 07:27:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/16/126773.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/126773.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/16/126773.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/126773.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/126773.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>2</span><span>、子窗口控件的通用使用方法</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>由于子窗口控件实际上就是窗口，大部分窗口函数对它们都是适用的，如可以用</span><span>EnableWindow</span><span>在灰化和允许状态之间切换，可以用</span><span>ShowWindow</span><span>在显示和隐藏之间切换，可以用</span><span>GetWindowText</span><span>和</span><span>SetWindowText</span><span>来改变上面的文字，也可以用</span><span>MoveWindow</span><span>来改变大小和移动位置等。在</span><span>Control.asm</span><span>中用&#8220;显示图片&#8221;复选框切换图片框的隐藏和显示，用的就是</span><span>ShowWindow</span><span>函数，处理&#8220;允许更换图片&#8221;复选框时切换&#8220;更换图片&#8221;按钮的状态，用的是</span><span>EnableWindow</span><span>函数。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>除了可以用对子窗口控件使用窗口的通用函数外，还可以使用针对它们的专用函数。下面介绍一些常用的函数。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在资源脚本文件中定义的是控件的</span><span>ID</span><span>，当这些子窗口控件被创建以后同样会有一个窗口句柄，但既然它们不是由我们由自己创建的，那么怎么知道它们的窗口句柄呢？有一个函数可以从</span><span>ID</span><span>中获取子窗口句柄：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetDlgItem, hDlg, dwIDDlgItem</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp; </span>hDlgItem, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>函数的输入参数是对话框句柄和</span><span>ID</span><span>值，返回值是子窗口句柄；反过来，有两种方法可以从子窗口句柄获取</span><span>ID</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>1</span><span>）</span><span>invoke GetDlgCtrolID, hWndCtrl<span> </span>;</span><span>输入子窗口句柄，返回值是控件</span><span>ID</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>2</span><span>）</span><span>invoke GetWindowLong, hWndCtrl, GWL_ID</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>当需要向控件发送消息的时候，当然可以先用</span><span>GetDlgItem</span><span>获取子窗口句柄再用</span><span>SendMessage</span><span>函数，但有一个函数更为简便：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hDlg, dwIDDlgItem, Msg, wParam, lParam</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>这个函数可以直接向控件发送消息，只需要在参数中指定对话框句柄和子窗口</span><span>ID</span><span>（注意：并没有</span><span>PostDlgItemMessage</span><span>这样的函数！）。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>如果要想知道在一个控件上按下了</span><span>Tab</span><span>键或</span><span>Shift+Tab</span><span>键会跳到哪一个控件上去，也就是说下一个或上一个</span><span>Tab</span><span>停留位在哪里，可以使用</span><span>GetNextDlgTabItem</span><span>函数：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke GetNextDlgTabItem, hDlg, hCtl, bPrevious</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov hWinNext, eax</span></p>
<p><span>.endif</span></p>
<p>&nbsp;</p>
<p><span>其中的</span><span>bPrevious</span><span>参数指定了搜索的方向；与之相似，使用</span><span>GetNextDlgGroupItem</span><span>函数可以返回下一个分组的位置：</span></p>
<p><span>invoke GetNextDlgGroupItem, hDlg, hCtl, bPrevious</span></p>
<p><span>.if eax</span></p>
<p><span><span>&nbsp;&nbsp; </span>mov hWinNext, eax</span></p>
<p><span>.endif</span></p>
&nbsp; <br>
<p><span><br>3</span><span>、使用单选钮和复选框</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>单选钮是互斥的选择钮，同一组的多个单选钮只能有一个被选中，单选钮的外形是一个圆形的标记加上文本，圆形中有黑点表示被选中。复选框不是互斥的，多个复选框的状态不会互相影响，复选框的外形是一个方框加上文本，方框中可以用有无对钩来表示是否被选中。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>单选钮和复选框控件都是基于</span><span>Button</span><span>类的，只不过它们的窗口风格分别是</span><span>BS_RADIOBUTTON</span><span>和</span><span>BS_CHECKBOX</span><span>。既然它们是特殊的&#8220;按钮&#8221;，所以和它们有关的函数都带有&#8220;</span><span>Button</span><span>&#8221;一词，查看一个单选钮或复选框是否被选中可以用下面的函数来检测：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke IsDlgButtonChecked, hDlg, nIDButton</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>函数的返回值可能是</span><span>BST_CHECKED</span><span>（选中状态），</span><span>BST_INDETERMINATE</span><span>（</span><span>3</span><span>态复选框的灰化状态）或</span><span>BST_UNCHECKED</span><span>（未选中状态）。也可以用向子窗口控件发送</span><span>BM_GETCHECK</span><span>消息的方法来检测，返回值和上面的函数是一样的。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>如果想设置单选钮或复选框的状态，可以使用下面的语句：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke CheckDlgButton, hDlg, nIDButton, uCheck</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>参数</span><span>uCheck</span><span>用</span><span>BST_CHECKED</span><span>，</span><span>BST_INDETERMINATE</span><span>或</span><span>BST_UNCHECKED</span><span>来表示需要设置的状态，含义同上。向控件发送</span><span>BM_SETCHECK</span><span>消息也可以取得同样的效果，这时消息的</span><span>wParam</span><span>中放置需要设置的状态。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>复选框是不互斥的，所以可以随意设置状态。而对于</span><span>BS_RADIOBUTTON</span><span>风格的单选钮来说，并不是把某个按钮设置为选中状态以后，同组的其他按钮就会自动变成非选中状态，所以用</span><span>CheckDlgButton</span><span>函数选中了一个单选钮以后，如果不是手动把同组的其他按钮全部改为非选中状态（逐个地调用</span><span>CheckDlgButton</span><span>），就会看到同时有两个单选钮是选中的。但把同组的所有单选钮逐个地设置显得有点麻烦，所以针对单选钮有一个专用函数：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke CheckRadioButton, hDlg, nIDFirstButton, nIDLastButton, nIDCheckButton</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>这个函数把</span><span>ID</span><span>在</span><span>nIDFirstButton</span><span>和</span><span>nIDLastButton</span><span>之间的单选钮全部设置为非选中状态，只有</span><span>nIDCheckButton</span><span>是选中状态，当然在使用中要注意将这一批</span><span>ID</span><span>定义为连续的数值。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>如果还嫌</span><span>CheckRadioButton</span><span>有点麻烦，还有一种最简单的办法——使用自动单选钮，同组的</span><span>AUTORADIOBUTTON</span><span>会随着用户选中一个而自动清除其他单选钮的状态，所以在程序中只需要在初始化的时候预设一次，其他时间就可以不必关心设置问题了，以后唯一用到的就是调用</span><span>IsDlgButtonChecked</span><span>检查状态了。</span></p>
<p>&nbsp;</p>
<p><span>4</span><span>、使用静态控件</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>静态控件是基于</span><span>Static</span><span>类的子窗口控件，之所以叫&#8220;静态&#8221;控件，是因为它是&#8220;安静&#8221;的——它们不向对话框发送</span><span>WM_COMMAND</span><span>消息，所以静态控件的</span><span>ID</span><span>一般是没有用处的，定义时常常将它们定义为</span><span>-1</span><span>，如果需要在程序中改变属性，那么也可以为静态控件指定一个唯一的</span><span>ID</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>资源脚本文件中可以使用缩写的基于</span><span>Static</span><span>类的有</span><span>LTEXT</span><span>，</span><span>CTEXT</span><span>，</span><span>RTEXT</span><span>（文本框）和</span><span>ICON</span><span>（图标框），除了这些常用的类型之外，</span><span>Static</span><span>类还可以用</span><span>CONTROL</span><span>语句通过指定不同的窗口风格派生出不同用途的控件来。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>下面说明静态控件的一些用法。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于文本框，文本长度超过边界的时候默认是自动换行的，但如果同时指定</span><span>SS_SIMPLE</span><span>风格的话，就不会自动换行。读者可以在程序中用</span><span>SetWindowText</span><span>或发送</span><span>WM_SETTEXT</span><span>消息来动态改变显示的文本，同样，也可以用</span><span>GetWindowText</span><span>或发送</span><span>WM_GETTEXT</span><span>消息来获取其中的文本。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>静态控件可以用来构筑简单的线条和图形，如果指定</span><span>SS_BLACKFRAME</span><span>，</span><span>SS_GRAYFRAME</span><span>或</span><span>SS_WHITEFRAME</span><span>风格，那么静态控件显示为填充的矩形，填充颜色分别是黑色、灰色或白色；而指定</span><span>SS_BLACKRECT</span><span>，</span><span>SS_GRAYRECT</span><span>或</span><span>SS_WHITERECT</span><span>风格的话，则显示为非填充的矩形框，边线颜色是黑色、灰色或白色。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>静态控件也可以用来做立体感的线条或边框，指定</span><span>SS_ETCHEDHORZ</span><span>风格显示为横线，指定</span><span>SS_ETCHEDVERT</span><span>风格显示为竖线，指定</span><span>SS_ETCHEDFRAME</span><span>风格则显示为立体的矩形框，视觉上的效果似于没有文字的</span><span>GROUPBOX</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>静态控件还有一个用途是做图形显示，当图形是图标的时候，用</span><span>ICON</span><span>语句就可能定义了，其默认的风格是</span><span>SS_ICON</span><span>，如果想使用位图，那么可以指定</span><span>SS_BITMAP</span><span>风格，例子程序中的图片框就是这样定义的。</span></p>
<p><span>CONTROL IDB_1, IDC_BMP, &#8220;Static&#8221;, SS_BITMAP | WS_CHILD | WS_VISIBLE, 5, 5, 40, 95</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在这里，&#8220;文字&#8221;部分指定位图资源的</span><span>ID</span><span>，前面已经把</span><span>Picture1.bmp</span><span>的资源</span><span>ID</span><span>定义为</span><span>IDB_1</span><span>，</span><span>IDC_BMP</span><span>是图片框自己的</span><span>ID</span><span>，如果不需要在程序中改变图片的话，那么这里可以定义为</span><span>-1</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在程序中可以通过向控件发送</span><span>STM_SETIMAGE</span><span>消息来设置新的图片，消息的</span><span>wParam</span><span>指定图片的格式，取值可以是</span><span>IMAGE_BITMAP</span><span>，</span><span>IMAGE_CURSOR</span><span>和</span><span>IMAGE_ICON</span><span>，分别对应新图片的格式，</span><span>lParam</span><span>是图片的句柄，如果是位图，</span><span>lParam</span><span>就是用</span><span>LoadBitmap</span><span>装入的位图句柄，同样，图片类型是光标和图标的时候，这里就是用</span><span>LoadCursor</span><span>和</span><span>LoadIcon</span><span>装入的句柄。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在例子程序中，用来改变图片框图片的语句是：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hWnd, IDC_BMP, STM_SETIMAGE, IMAGE_BITMAP, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax</span><span>中是位图句柄，</span><span>IDC_BMP</span><span>是图片框的</span><span>ID</span><span>，</span><span>wParam</span><span>用</span><span>IMAGE_BITMAP</span><span>表示要设置的图片类型是位图。</span></p>
<p>&nbsp;</p>
<p><span>5</span><span>、使用文本编辑控件</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>文本编辑控件是基于</span><span>Edit</span><span>类的控件，可以用缩写</span><span>EDITTEXT</span><span>定义，读者可以在文本编辑控件中输入并编辑文本。每当用户在文本编辑控件中输入一个字符的时候，控件就会向对话框过程发送一个</span><span>WM_COMMAND</span><span>消息，所以在例子程序中，当在自定义文字的编辑框中每输入一个字，标题栏文字就会马上改变。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>要获取编辑框中的文本有多种方法，可以用</span><span>GetWindowText</span><span>，也可以用发送</span><span>WM_GETTEXT</span><span>消息的办法，要设置文本，同样可以用</span><span>SetWindowText</span><span>或发送</span><span>WM_SETTEXT</span><span>，但最简便的办法还是使用下面的函数：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke GetDlgItemText, hDlg, nIDDlgItem, lpString, nMaxCount<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>取文本</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SetDlgItemText, hDlg, nIDDlgItem, lpString<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置文本</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>lpString</span><span>是放置字符的缓冲区地址，用</span><span>GetDlgItemText</span><span>函数来获取文本的时候，要用</span><span>nMaxCount</span><span>参数指定缓冲区的最大长度，以免获取的文本长度超过缓冲区长度引起溢出，设置的时候若使用</span><span>SetDlgItemText</span><span>函数时就不需要这个参数。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在实际使用中，经常要在文本编辑控件中输入输出数值型参数，将文本转换为数值比较麻烦，把数值转换为文本也要经过一个</span><span>wsprintf</span><span>调用，为了简化操作，</span><span>Windows</span><span>提供了两个函数来处理这个问题：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SetDlgItemInt, hDlg, nIDDlgItem, uValue, bSigned<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置控件中的数值</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke GetDlgItemInt, hDlg, nIDDlgItem, lpTranslated, bSigned<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>取控件中的数值</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SetDlgItemInt</span><span>函数将</span><span>uValue</span><span>参数先转换成字符串格式，然后设置到文本编辑控件中，</span><span>bSigned</span><span>参数指定了</span><span>uValue</span><span>的格式，如果是</span><span>TRUE</span><span>的话，表示</span><span>uValue</span><span>是有符号数；是</span><span>FALSE</span><span>的话，表示</span><span>uValue</span><span>是无符号数。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>GetDlgItemInt</span><span>函数则将对话框中的文本转换成数值型返回，同样，用</span><span>bSigned</span><span>指定转换的方式，</span><span>TRUE</span><span>表示按照符号数格式转换，这时函数会检测文本的第一个字符是不是负号；</span><span>FALSE</span><span>则按照无符号数转换。参数</span><span>lpTranslated</span><span>是指向一个</span><span>dword</span><span>型变量的指针，</span><span>GetDlgItemInt</span><span>会在这个变量中返回</span><span>BOOL</span><span>类型值表示函数是否调用成功，成功则返回</span><span>TRUE</span><span>，有这样一个参数的原因是函数的返回值用来返回转换后的数值了，以至于没有地方可以表示函数是否执行成功。当然，</span><span>lpTranslated</span><span>参数也可以输入</span><span>NULL</span><span>，这样，当函数返回</span><span>0</span><span>的时候就无法知道是文本框是&#8220;</span><st1:chmetcnv UnitName="&#8221;" SourceValue="0" HasSpace="False" Negative="False" NumberType="1" TCSC="0" w:st="on"><span>0</span><span>&#8221;</span></st1:chmetcnv><span>还是文本不符合格式造成转换失败。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SetDlgItemInt</span><span>和</span><span>GetDlgItemInt</span><span>函数不仅适用于文本编辑控件，所有对其上面的文本可以修改的控件都可以使用它们。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>使用文本编辑控件的时候，文本的长度也是个需要注意的问题。如果控件的宽度定义得过窄，当字符填充到最右边的时候，编辑框就不允许继续输入了，为了继续输入并让文本自动卷动，可以指定</span><span>WS_HSCROLL</span><span>风格；反之，定义</span><span>WS_HSCROLL</span><span>风格后输入文本的长度不受限制又不好，那么可以用向控件发送</span><span>EM_LIMITTEXT</span><span>消息的方式来设定最大长度。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>下面的例子将</span><span>IDC_EDIT</span><span>的输入最大长度定为</span><span>10</span><span>个字符：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hDlg, IDC_EDIT, EM_LIMITTEXT, 10, NULL</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>另外，有时候可能需要把编辑框设置为只读的（和灰化不同，灰化的编辑框中文本无法进行任何操作，包括卷动操作，而只读的仅仅是不能修改），要把初始状态定义为只读的，只需在定义语句中加上</span><span>ES_READONLY</span><span>风格，在程序中需要动态改变只读状态可以发送</span><span>EM_SETREADONLY</span><span>消息，下面的第一句把编辑框设为只读，第二句把编辑框改回到可写状态：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hDlg, IDC_EDIT, EM_SETREADONLY, TRUE, NULL ;</span><span>只读</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hDlg, IDC_EDIT, EM_SETREADONLY, FALSE, NULL;</span><span>可写</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>文本编辑框在默认状态下是单行的，也可以通过加上</span><span>EM_MULTILINE</span><span>风格变成多行的，这时可以同时加上</span><span>WS_VSCROLL</span><span>风格显示一个垂直方向的滚动条。</span></p>
<p>&nbsp;</p>
<p><span>6</span><span>、使用滚动条</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条有水平和垂直两种，默认的</span><span>SCROLLBAR</span><span>语句定义的是水平的滚动条，它的默认风格是</span><span>SBS_HORZ</span><span>，例子程序中用下面的语句定义了一个水平滚动条：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SCROLLBAR IDC_SCROLL, 6, 118, 125, 10</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>如果要定义垂直的滚动条，那么要指明</span><span>SBS_VERT</span><span>风格：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SCROLLBAR IDC_SCROLL, x, y, </span><span>宽度</span><span>, </span><span>高度</span><span>, SBS_VERT</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>和其他子窗口控件发送</span><span>WM_COMMAND</span><span>消息不同，水平滚动条向对话框窗口发送</span><span>WM_HSCROLL</span><span>消息，而垂直滚动条则发送</span><span>WM_VSCROLL</span><span>消息，所以针对两种方式的滚动条要分别处理不同的消息。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WM_xSCROLL</span><span>消息的参数如下所示：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wParam</span><span>的低</span><span>16</span><span>位</span><span> = nScrollCode<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>动作码</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wParam</span><span>的高</span><span>16</span><span>位</span><span> = nPos<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>滚动条当前位置</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>lParam = hwndScrollBar<span>&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; </span>;</span><span>滚动条控件的窗口句柄</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>其中</span><span>nScrollCode</span><span>代表了滚动条的当前动作，定义值及其含义如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_BOTTOM<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条移到了最下</span><span>/</span><span>右边。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_ENDSCROLL<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>用户停止了滚动动作。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_THUMBPOSITION<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条被拖动到某处。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_THUMBTRACK<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条在拖动中。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_TOP<span>&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; </span></span><span>滚动条移到了最上</span><span>/</span><span>左边。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_LINELEFT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条左移了一格（对于水平滚动条）。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_LINERIGHT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条右移了一格（对于水平滚动条）。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_PAGELEFT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条左移了一页（对于水平滚动条）。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_PAGERIGHT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条右移了一页（对于水平滚动条）。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_LINEDOWN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条下移了一格（对于垂直滚动条）。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_LINEUP<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条上移了一格（对于垂直滚动条）。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_PAGEDOWN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条下移了一页（对于垂直滚动条）。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SB_PAGEUP<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>滚动条上移了一页（对于垂直滚动条）。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>nPos</span><span>的值只有当动作码是</span><span>SB_THUMBPOSITION</span><span>或</span><span>SB_THUMBTRACK</span><span>时才有效，其他的时候为</span><span>0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>第一眼看到</span><span>SB_xxx</span><span>动作码的时候，读者可能会以为水平滚动条和垂直滚动条的动作码是不相同的——水平滚动条是</span><span>SB_xxxLEFT</span><span>、</span><span>SB_xxxRIGHT</span><span>，而垂直滚动条是</span><span>SB_xxxUP</span><span>、</span><span>SB_xxxDOWN</span><span>，但在</span><span>Windows.inc</span><span>中查看一下就可以发现，</span><span>SB_xxxLEFT</span><span>和</span><span>SB_xxxUP</span><span>在数值上是相同的，</span><span>SB_xxxRIGHT</span><span>和</span><span>SB_xxxDOWN</span><span>也是如此，所以不同定义方法只是为了直观起见而已。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>以水平滚动条为例，处理滚动条消息的代码一般是如下结构：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif&nbsp;eax == WM_HSCROLL<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>窗口的消息处理分支，</span><span>eax</span><span>为</span><span>wMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov&nbsp;eax, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp; </span>eax&nbsp;== hWnd</span><span>滚动条</span><span>1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>mov&nbsp;eax, wParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if&nbsp;ax == SB_LINELEFT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dec&nbsp;</span><span>位置变量</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif&nbsp;ax == SB_LINERIGHT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc<span>&nbsp;&nbsp; </span></span><span>位置变量</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif&nbsp;ax == SB_PAGELEFT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sub<span>&nbsp;&nbsp; </span></span><span>位置变量，页长</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>.elseif&nbsp;ax == SB_PAGERIGHT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add<span>&nbsp;&nbsp; </span></span><span>位置变量，页长</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif&nbsp;ax == SB_THUMBPOSITION || ax == SB_THUMBTRACK</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp; </span>eax, wParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr<span>&nbsp;&nbsp;&nbsp; </span>eax, 16</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp; </span></span><span>位置变量，</span><span>eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif&nbsp;eax == hWnd</span><span>滚动条</span><span>2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>处理滚动条</span><span>2</span><span>的代码，同上面的结构</span></p>
<p><span>&#8230;</span></p>
<p><span>.endif</span></p>
<p>&nbsp;</p>
<p><span>在例子程序</span><span>Control.asm</span><span>中只定义了一个滚动条，所有的消息肯定都是它发出的，所以去掉了判断</span><span>lParam</span><span>是哪个滚动条的步骤直接处理</span><span>wParam</span><span>中的动作码。</span></p>
<p>&nbsp;</p>
<p><span>在用户按动滚动条后，滚动条不会自己移动位置，它只是将用户的动作以</span><span>WM_xSCROLL</span><span>消息的形式反馈给程序，真正要移动它还是要靠程序来设置，所以代码中要根据不同的动作首先计算新的位置，并判断新的位置是否越界，例子程序中的这些代码判断新的位置是否超出</span><span>0~100</span><span>的范围，如果是，则校正到</span><span>0~100</span><span>之间：</span></p>
<p><span>cmp dwPos, 0</span></p>
<p><span>jge&nbsp;@F</span></p>
<p><span>mov dwPos, 0</span></p>
<p><span>@@:</span></p>
<p><span>cmp dwPos, 100</span></p>
<p><span>jle&nbsp;@F</span></p>
<p><span>mov dwPos,100</span></p>
<p>&nbsp;</p>
<p><span>在介绍</span><span>MASM</span><span>语句的时候提到过，</span><span>.if dwPos &gt; 0</span><span>语句只可以用来比较无符号数，所以在这里使用</span><span>cmp</span><span>指令自己测试分支而不是使用</span><span>.if</span><span>伪指令。</span></p>
<p><span>不计算好新位置的时候要将位置设置回去，用户才会看到滚动条移动了，方法是向滚动发送</span><span>SBM_SETPOS</span><span>消息：</span></p>
<p><span>invoke&nbsp;SendDlgItemMessage, hWnd, IDC_SCROLL, SBM_SETPOS, dwPos, TRUE</span></p>
<p><span>最后一个参数为</span><span>TRUE</span><span>表示设置后重新绘画滚动条。</span></p>
<p><span>在初始化的时候，要给滚动条发送</span><span>SBM_SETRANGE</span><span>消息来设定滚动范围：</span></p>
<p><span>invoke SendDlgItemMessage, hWnd, IDC_SCROLL, SBM_SETRANGE, </span><span>最小值</span><span>, </span><span>最大值</span></p>
<p>&nbsp;</p>
<p><span>如果需要获取滚动条的信息，可以尝试发送下面两个消息：</span><span>SBM_GETPOS</span><span>可以获取滚动条的当前位置，也就是上一次用</span><span>SBM_SETPOS</span><span>设置的值；</span><span>SBM_GETRANGE</span><span>可以获取滚动的范围，也就是用</span><span>SBM_SETRANGE</span><span>设置的值。</span></p>
<p>&nbsp;</p>
<p><span>7</span><span>、使用组合框</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>顾名思义，组合框是一个&#8220;组合&#8221;起来的东西，它由一个可供选择的列表和一个可供输入的</span><span>edit</span><span>类组合而成。组合框让用户既可以自己输入文本，也可以选择列表中的某一项当做输入。用不同的风格定义可以产生</span><span>3</span><span>种类型的组合框。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CBS_SIMPLE</span><span>风格的组合框，它的上面可以输入文本，下面的列表可供选择预设文本；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CBS_DROPDOWN</span><span>风格的组合框，上面同样可以输入文本，但下面的列表是下拉式的，平时处于收起状态，点击编辑框右边的三角形才会拉下来；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CBS_DROPDOWNLIST</span><span>风格的组合框，它仅是一个下拉的选择框，上面的框中不允许输入文字。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>组合框中还有几种常用的、可以附加的风格：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CBS_AUTOHSCROLL<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>输入过长的文本时输入框自动卷动。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CBS_LOWERCASE<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>自动将所有的文本转换成小写。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CBS_SORT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>自动将插入的文本项排序。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CBS_UPPERCASE<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>自动将所有的文本转换成大写。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>组合框中列表框部分的文字添加、项目的选择等操作都是通过发送消息来完成的，主要的消息如下表所示：</span></p>
<p align=center><span>组合框的消息</span></p>
<table cellSpacing=0 cellPadding=0 width=559 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=127>
            <p><span>消息</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>wParam</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>lParam</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>说明</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_ADDSTRING</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>字符串地址</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>把一个字符串添加到列表中</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_INSERTSTRING</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>字符串地址</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>把一个字符串插入到列表中</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_FINDSTRING</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>开始查找的位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>查看的字符串</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>在列表中查找以</span><span>lParam</span><span>字符串开头的项，找到则返回位置索引，未找到则返回</span><span>CB_ERP</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_FINDSTRINGEXACT</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>查找的字符串</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>精确查找字符串</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_DELETESTRING</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>删除一个列表项</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_RESETCONTENT</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>删除所有的列表项</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_GETLBTEXT</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>缓冲区地址</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>获取指定列表项的字符串，缓冲区必须足够大</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_GETLBTEXTLEN</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>获取指定列表项的字符串长度</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_GETCOUNT</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>获取列表项的总项数</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_SETCURSEL</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>选中一个列表项，并将列表项中的文字拷贝到编辑控件中</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_SELECTSTRING</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>开始查找的位置索引</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>字符串地址</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>查找以</span><span>lParam</span><span>指定的字符串开始的列表项，如果找到则选中它并将字符串拷贝到编辑控件中</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_GETCURSEL</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>获取当前选中的位置索引，没有选中的项目则返回</span><span>CB_ERR</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_SHOWDROPDOWN</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>状态</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>打开（状态为</span><span>TRUE</span><span>）或收起（状态为</span><span>FALSE</span><span>）下拉列表</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=127>
            <p><span>CB_GETDROPPEDSTATE</span></p>
            </td>
            <td vAlign=top width=132>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=96>
            <p><span>0</span></p>
            </td>
            <td vAlign=top width=204>
            <p><span>检测列表的当前下拉状态，返回</span><span>TRUE</span><span>表示拉下，</span><span>FALSE</span><span>表示收起</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>当用户在组合框中进行选择操作时，</span><span>Windows</span><span>向对话框过程发送</span><span>WM_COMMAND</span><span>消息，消息中</span><span>wParam</span><span>参数的低</span><span>16</span><span>位是组合框</span><span>ID</span><span>，高</span><span>16</span><span>位是通知码，用来表示用户的操作，通知码的定义如下表所示。</span></p>
<p align=center><span>用户操作组合框后的通知码</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=139>
            <p><span>通知码</span></p>
            </td>
            <td vAlign=top width=420>
            <p><span>说明</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>CBN_SELCHANGE</span></p>
            </td>
            <td vAlign=top width=420>
            <p><span>用户将要选择一个项目（鼠标移动到了这个项目上）</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>CBN_CLOSEUP</span></p>
            </td>
            <td vAlign=top width=420>
            <p><span>下拉列表关闭（可能是选择完成也可以是取消选择）</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>CBN_SELENDOK</span></p>
            </td>
            <td vAlign=top width=420>
            <p><span>用户完成选择项目</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>CBN_SELENDCANCEL</span></p>
            </td>
            <td vAlign=top width=420>
            <p><span>用户取消选择（鼠标移动到了某个项目上，但并没有按下而是点击了其他控件，或按动了</span><span>Esc</span><span>键）</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>CBN_DBLCLK</span></p>
            </td>
            <td vAlign=top width=420>
            <p><span>在</span><span>CBS_SIMPLE</span><span>的组合框中双击了一个列表项</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=139>
            <p><span>CBN_DROPDOWN</span></p>
            </td>
            <td vAlign=top width=420>
            <p><span>用户打开了下拉框（按动了编辑框的下拉按钮）</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>如果想在用户选择了一个项目后做相应的动作，最好的办法就是处理</span><span>CBN_SELENDOK</span><span>通知码，因为这才意味着用户真正完成了一个选择动作，例子程序中就是这样处理的：</span></p>
<p><span>.elseif&nbsp;ax == IDC_TITLETEXT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>在</span><span>WM_COMMAND</span><span>消息中</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr&nbsp;eax,16</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if&nbsp;ax == CBN_SELENDOK</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendDlgItemMessage, hWnd, IDC_TITLETEXT, CB_GETCURSEL, 0, 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp; </span>;</span><span>根据返回的</span><span>eax</span><span>值做相应动作</span><span>&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>以上的操作都是针对下拉列表部分的，另外也有很多消息是针对组合框中的编辑控件的，对组合框的窗口句柄发送</span><span>WM_GETTEXT</span><span>和</span><span>WM_SETTEXT</span><span>，操作的对象就是组合框的编辑控件；如果要限制控件中文本的最大输入长度，可以发送</span><span>CB_LIMITTEXT</span><span>的消息，这时候</span><span>wParam</span><span>参数指定最大数量；当用户在编辑框中编辑文本的时候，</span><span>Windows</span><span>在用户输入之后、字符显示之前会发送</span><span>CBN_EDITUPDATE</span><span>通知码；当字符在编辑框中显示以后，又会发送</span><span>CBN_EDITCHANGE</span><span>通知码。所以在处理</span><span>WM_COMMAND</span><span>消息时通过处理这两个通知码可以检测到用户的输入操作。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>组合框是子窗口控件中比较复杂的一种，这里仅介绍了常用的消息和通知码。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/126773.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-16 15:27 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/16/126773.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--对话框--在对话框中使用子窗口控件(1)</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/16/126769.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Thu, 16 Sep 2010 07:25:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/16/126769.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/126769.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/16/126769.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/126769.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/126769.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;使用资源—对话框—在对话框中使用子窗口控件子窗口控件是一些Windows预定义类，它们实际上就是一个个以对话框为父窗口的子窗口。对于程序员来说，在对话框中使用它们的时候并不需要手工去逐一创建，只需要在对话框中定义就可以了，&#8220;对话框管理器&#8221;会在初始化对话框的时候，根据定义语句自动建立所有的子窗口。&nbsp;1、子窗口控件的定义&nbsp;&n...&nbsp;&nbsp;<a href='http://www.cppblog.com/luqingfei/archive/2010/09/16/126769.html'>阅读全文</a><img src ="http://www.cppblog.com/luqingfei/aggbug/126769.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-16 15:25 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/16/126769.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编中常见的一些错误信息</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/15/126648.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 15 Sep 2010 04:49:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/15/126648.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/126648.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/15/126648.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/126648.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/126648.html</trackback:ping><description><![CDATA[<p>半个月没有学习Win32汇编了，调试能力弱了不少。<br>一段代码居然死活编译不通过，报错： error A2022: instruction operands must be the same size<br><br>在网上搜得这一篇好文，收藏之。<br><br>ml.exe错误信息</p>
<p>FATAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;严重错误<br>cannot open file&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;不能打开文件 <br>I/O error closing file&nbsp;&nbsp;&nbsp;I/O错误 正在关闭文件<br>I/O error writing file&nbsp;&nbsp;&nbsp;I/O错误 正在写文件<br>I/O error reading file&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; I/O错误 正在读取文件<br>out of memory&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 缺少内存<br>assembler limit : macro parameter name table full&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 汇编限制：宏参数名表已满<br>invalid command-line option&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 无效命令行参数<br>nesting level too deep&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 嵌套过深<br>unmatched macro nesting&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不正确的宏嵌套<br>line too long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 行太长<br>unmatched block nesting&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不正确的区、段嵌套<br>directive must be in control block&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指令必须在控制段<br>error count exceeds 100; stopping assembly&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 错误数超过100，停止汇编<br>invalid numerical command-line argument&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 无效命令行参数<br>too many arguments&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; 太多参数、定义、冲突<br>statement too complex&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; 声明太复杂<br>Internal Assembler Error&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>missing source filename&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; 找不到源文件名<br>COFF error writing file&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COFF错误，正在写文件<br>invalid debug and browser data; file exceeds line limit&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不能排除故障和浏览数据；文件超过行限制<br>cannot find link.exe&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>cannot find cvpack.exe&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; 找不到cvpack.exe <br>SEVERE&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>memory operand not allowed in context&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内存操作数无法载入上下文环境<br>immediate operand not allowed&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当前操作数无法载入<br>cannot have more than one ELSE clause per IF block&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IF段只能有一个ELSE从句<br>extra characters after statement&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>symbol type conflict&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>symbol redefinition&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>undefined symbol&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>non-benign record redefinition&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没有利于记录的定义<br>syntax error&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; 语法错误<br>syntax error in expression&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 表达式存在语法错误<br>invalid type expression&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 无效的类型表达式<br>distance invalid for word size of current segment&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当前区、段的大小命令无效<br>PROC, MACRO, or macro repeat directive must precede LOCAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PROC, MACRO, 或 macro repeat指令必须在LOCAL之前<br>.MODEL must precede this directive&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; .MODEL必须在指令之前<br>cannot define as public or external&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不能定义为公有或外部的<br>segment attributes cannot change&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 区、段属性不能更换<br>expression expected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 预期表达式<br>operator expected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 预期操作数<br>invalid use of external symbol&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用了无效的外部符号<br>operand must be RECORD type or field&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作数必须是RECORD类型或域<br>identifier not a record&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没有记录标示符<br>record constants may not span line breaks&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 连续记录不能超过行间隔<br>instruction operands must be the same size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 命令操作数必须是一样的长度<br>instruction operand must have size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 命令操作数必须有长度<br>invalid operand size for instruction&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作数长度对于指令无效<br>operands must be in same segment&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作数必须在相同的段<br>constant expected&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>operand must be a memory expression&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作数必须是一个内存表达式<br>expression must be a code address&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 表达式必须是一个代码地址<br>multiple base registers not allowed&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不允许多重基础寄存器<br>multiple index registers not allowed&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不允许多重标志寄存器<br>must be index or base register&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 必须是基础或标志寄存器<br>invalid use of register&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用的寄存器无效<br>invalid INVOKE argument&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 无效的INVOKE符号<br>must be in segment block&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 必须在区、段、块中<br>DUP too complex&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; DUP太复杂<br>too many initial values for structure&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 太多结构的基础资料<br>statement not allowed inside structure definition&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 声明不允许在结构里面<br>missing operand for macro operator&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 找不到宏的操作数<br>line too long<br>segment register not allowed in context&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上下文不允许有寄存器<br>string or text literal too long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 文本或字符串太长<br>statement too complex&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 声明太复杂<br>identifier too long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 标识符太长<br>invalid character in file&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 文件里有无效字符<br>missing angle bracket or brace in literal&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 语句里找不到同样的括弧或框架<br>missing single or double quotation mark in string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 找不到单引号或双引号<br>empty (null) string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没有字符串<br>nondigit in number&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没有总数<br>syntax error in floating-point constant&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不确定的指向中有语法错误<br>real or BCD number not allowed&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不允许real或BCD编码<br>text item required&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 必须的文本项<br>forced error&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>forced error : value equal to 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 标准等于零<br>forced error : value not equal to 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 标准不等于零<br>forced error : symbol not defined&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 符号没有定义<br>forced error : symbol defined&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 符号已定义<br>forced error : string blank&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字符串是空的<br>forced error : string not blank&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字符串不是空的<br>forced error : strings equal&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字符串是相同的<br>forced error : strings not equal&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字符串不是相同的<br>[ELSE]IF2/.ERR2 not allowed : single-pass assembler&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ELSE]IF2/.ERR2不允许单独汇编<br>expression too complex for .UNTILCXZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .UNTILCXZ表达式太复杂<br>can ALIGN only to power of 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 仅能对齐到2的幂<br>structure alignment must be 1, 2, 4, 8, or 16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 结构对齐必须是1，2，4，8或16<br>expected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 预定义&nbsp; <br>incompatible CPU mode and segment size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不匹配的CPU模式和段尺寸<br>LOCK must be followed by a memory operation&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOCK指令必须跟在内存操作之后<br>instruction prefix not allowed&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不允许的命令前缀<br>no operands allowed for this instruction&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指令没有操作数<br>invalid instruction operands&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 无效的指令操作数<br>initializer magnitude too large for specified size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 初始指定尺寸太大<br>cannot access symbol in given segment or group&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在特定的段或类不能存取符号<br>operands have different frames&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作数存在不同的结构<br>cannot access label through segment registers&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在段寄存器中不能存取标记<br>jump destination too far&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>jump destination must specify a label&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 跳转目标必须指定一个标记<br>instruction does not allow NEAR indirect addressing&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指令不允许近间接寻址<br>instruction does not allow FAR indirect addressing&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指令不允许远间接寻址<br>instruction does not allow FAR direct addressing&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指令不允许远直接寻址<br>jump distance not possible in current CPU mode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 跳转距离不适合当前CPU模式<br>missing operand after unary operator&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一元运算符之后找不到操作数<br>cannot mix 16- and 32-bit registers&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不能结合16位和32位寄存器<br>invalid scale value&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; 无效范围标准<br>constant value too large&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 连续标准太多<br>instruction or register not accepted in current CPU mode&nbsp;&nbsp; 当前CPU模式不认可的指令或寄存器<br>reserved word expected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 预期的保留字<br>instruction form requires 80386/486&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指令需要80386/486指示&nbsp;&nbsp; <br>END directive required at end of file&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END指令必须在文件结尾<br>too many bits in RECORD&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; 太多位在记录里<br>positive value expected&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; 预期的明确的标准<br>index value past end of string&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>count must be positive or zero&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>count value too large&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; 计数标准太多<br>operand must be relocatable&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; 操作数必须是转移表<br>constant or relocatable label expected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 预期的转移表或连续的<br>segment, group, or segment register expected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 预期的段，类型或段寄存器<br>segment expected&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; 预期的区段<br>invalid operand for OFFSET&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; OFFSET操作数无效<br>invalid use of external absolute&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>segment or group not allowed&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>cannot add two relocatable labels&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>cannot add memory expression and code label&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不能增加内存表达式和代码标记</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/126648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-15 12:49 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/15/126648.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--对话框（一）</title><link>http://www.cppblog.com/luqingfei/archive/2010/09/01/125536.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 01 Sep 2010 08:57:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/09/01/125536.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/125536.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/09/01/125536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/125536.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/125536.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>使用资源</span><span>—</span><span>对话框</span></p>
<p>&nbsp;</p>
<p><span>简介</span></p>
<p><span>顾名思义，对话框完成的是&#8220;对话&#8221;功能，应用程序一般建立一个主窗口用做工作界面，大部分的工作分在主窗口的客户区完成，但程序往往需要和用户交互，如输入参数和输入文本等，这些界面不必要全部放在主窗口中，习惯的做法是通过选择菜单项弹出一个窗口，然后在这个窗口中完成对话，这个窗口就是&#8220;对话框&#8221;，对话框中的按钮、文本框和图标等称为&#8220;子窗口控件&#8221;。</span></p>
<p>&nbsp;</p>
<p><span>为了提示用户，习惯于在会引出对话框的菜单项后面加上省略号。如&#8220;文件&#8221;菜单中的&#8220;另存为</span><span>&#8230;</span><span>&#8221;表示会引出一个选择文件名的对话框，所以&#8220;另存为&#8221;</span><span>3</span><span>个字后面加了个省略号。对话框最典型的例子就是写字板&#8220;查找&#8221;菜单弹出的窗口以及</span><span>IE</span><span>浏览器中选择&#8220;</span><span>Internet </span><span>选项&#8221;菜单项弹出的设置窗口。</span></p>
<p>&nbsp;</p>
<p><span>1</span><span>、对话框的类型</span></p>
<p><span>对话框分两类：</span><span>modal</span><span>对话框和</span><span>modeless</span><span>对话框，翻译成中文就是&#8220;模态的&#8221;和&#8220;非模态的&#8221;（也有的地方翻译成&#8220;模式的&#8221;和&#8220;非模式的&#8221;，</span><span>Visual FoxPro</span><span>中文版式就是这样），它们之间的区别在于是否允许用户在不同窗口间进行切换：当显示非模态对话框时，用户可以随意在这个对话框和其它窗口之间切换；而显示一个模态对话框时，用户在关闭对话框之前不允许切换到同一程序的其它窗口中，但可以切换到其他程序的窗口中；如果显示的是操作系统所属的模态对话框（即&#8220;系统模态的&#8221;），则切换到其他任何程序的窗口都是不允许的。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>在资源文件中定义对话框，然后在程序中利用这个模板创建对话框，模态对话框和非模态对话框的资源定义是相同的，只是创建时调用的函数不同而已。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、对话框的工作原理</span></p>
<p><span>很明显，对话框和普通窗口之间有很多相似之处，实际上对话框就是基于窗口的，对话框的窗口风格使用的就是普通窗口的风格定义，对话框也有一个类似于窗口过程的对话框过程，但对话框和普通窗口在实现上又有很多不同之处，模态对话框和非模态对话框的实现也是不同的。普通的窗口在建立之前需要用</span><span>RegisterClass</span><span>注册一个窗口类，然后用</span><span>CreateWindow</span><span>建立窗口，建立窗口所需的参数如窗口风格、大小位置和窗口过程地址等由窗口类以及</span><span>CreateWindow</span><span>中的参数共同提供。</span></p>
<p>&nbsp;</p>
<p><span>建立对话框的时候并不使用</span><span>CreateWindow</span><span>函数，取而代之，建立模态对话框的函数是</span><span>DialogBoxParam</span><span>，建立非模态对话框的函数是</span><span>CreateDialogParam</span><span>，调用这两个函数创建对话框窗口之前不需要注册对话框的窗口类。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>在这两个函数的内部调用</span><span>CreateWindowEx</span><span>来建立对话框，使用的风格、大小和位置参数取自资源中定义的对话框模板，使用的窗口类则是</span><span>Windows</span><span>内部定义的类，如果读者用一些工具去查看，会发现类名是&#8220;</span><span>#<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="32770" UnitName="&#8221;">32770<span><span>&#8221;</span></span></st1:chmetcnv><span>之类的字符串，在这个名字<span>奇特的窗口类中，窗口过程被定义到了</span></span>Windows</span><span>内部的&#8220;对话框管理器&#8221;代码中，</span><span>Windows</span><span>在这里处理对话框的大部分消息，如维护客户区的刷新，键盘接口（按</span><span>Tab</span><span>键在不同子窗口之间切换、按回车调用默认按钮等），对话框管理器在初始化对话框时会根据对话框模板中定义的子窗口控件建立对话框中所有的子窗口。</span></p>
<p>&nbsp;</p>
<p><span>用户程序中的对话框过程是由对话框管理器调用的，在处理消息前，对话框管理器会先调用用户指定的对话框过程，再根据对话框过程的返回值决定是否处理它们。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>对模态对话框，和非模态对话框的处理有些不同。在创建并显示模态对话框后，</span><span>Windows</span><span>会为它在内部建立一个消息循环，在这个消息循环中把消息发送给对话框管理器，对话框管理器在处理消息的过程中会调用用户定义的对话框过程，当对话框关闭的时候，</span><span>Windows</span><span>退出内建的消息循环，并从</span><span>DialogBoxParam</span><span>函数返回。而对于非模态对话框，</span><span>CreateDialogParam</span><span>函数在创建对话框后直接返回，对话框窗口的消息是通过用户程序中的消息循环派送的。</span></p>
<p>&nbsp;</p>
<p><span>由于模态对话框的特征，使得用户它来做小程序的主窗口非常方便，因为用一句</span><span>DialogBoxParam</span><span>函数就可以搞定了，既不用注册窗口类，也不用写消息循环，这对看到创建窗口的几十句代码就烦的读者来说可真是个福音，这种方法的缺点就是无法使用依赖消息循环来完成的功能，很明显，加速键就不能用了。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>对话框的资源定义</span></p>
<p><span>对话框资源定义的语法</span></p>
<p><span>在资源脚本中定义对话框的语法是：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对话框</span><span>ID<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DIALOG<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>[DISCARDABLE]&nbsp;x</span><span>坐标</span><span>, y</span><span>坐标</span><span>, </span><span>宽度</span><span>, </span><span>高度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>[</span><span>可选属性</span><span>]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>BEGIN</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>子窗口控件</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>END</span></p>
<p>&nbsp;</p>
<p><span>对话框中的子窗口控件语句定义在</span><span>BEGIN/END</span><span>（当然也可以以用花括号）之中，在这之前，可以定义对话框的一些可选属性，每种属性单独用一行定义，常用的可选属性如下表所示：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对话框的可选属性</span></p>
<table cellSpacing=0 cellPadding=0 width=552 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=72>
            <p><span>属性</span></p>
            </td>
            <td vAlign=top width=156>
            <p><span>定义语法</span></p>
            </td>
            <td vAlign=top width=324>
            <p><span>说明</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=72>
            <p><span>标题文字</span></p>
            </td>
            <td vAlign=top width=156>
            <p><span>CAPTION &#8220;</span><span>文字</span><span>&#8221;</span></p>
            </td>
            <td vAlign=top width=324>
            <p><span>定义显示在窗口标题栏上的文字</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=72>
            <p><span>窗口类</span></p>
            </td>
            <td vAlign=top width=156>
            <p><span>CLASS &#8220;</span><span>类名</span><span>&#8221;</span></p>
            </td>
            <td vAlign=top width=324>
            <p><span>定义对话框窗口使用的窗口类，如果不定义，则使用</span><span>Windows</span><span>内建的类</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=72>
            <p><span>窗口风格</span></p>
            </td>
            <td vAlign=top width=156>
            <p><span>STYLE </span><span>风格组合</span></p>
            </td>
            <td vAlign=top width=324>
            <p><span>定义对话框的窗口风格，同</span><span>CreateWindowEx</span><span>中的</span><span>dwStyle</span><span>参数</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=72>
            <p><span>扩展风格</span></p>
            </td>
            <td vAlign=top width=156>
            <p><span>EXSTYLE</span><span>风格组合</span></p>
            </td>
            <td vAlign=top width=324>
            <p><span>定义对话框的扩展窗口风格，同</span><span>CreateWindowEx</span><span>中的</span><span>dwExStyle</span><span>参数</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=72>
            <p><span>字体</span></p>
            </td>
            <td vAlign=top width=156>
            <p><span>FONT </span><span>大小</span><span>, &#8220;</span><span>字体名</span><span>&#8221;</span></p>
            </td>
            <td vAlign=top width=324>
            <p><span>定义对话框包括子窗口控件使用的字体</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=72>
            <p><span>菜单</span></p>
            </td>
            <td vAlign=top width=156>
            <p><span>MENU </span><span>菜单</span><span> ID</span></p>
            </td>
            <td vAlign=top width=324>
            <p><span>对话框中使用的菜单，菜单</span><span>ID</span><span>在同一个资源脚本文件中定义</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span>本节例子程序的资源脚本文件</span><span>Dialog.rc</span><span>定义如下：</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>#include<span>&nbsp;&nbsp;&nbsp; </span>&lt;resource.h&gt;</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>#define<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ICO_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>0x1000<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span>图标</span></p>
<p><span>#define<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DLG_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>ICO_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ICON<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>"Main.ico"</span></p>
<p><span>//&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>DLG_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DIALOG&nbsp;50, 50, 113, 64</span></p>
<p><span>STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU</span></p>
<p><span>CAPTION "</span><span>对话框模板</span><span>"</span></p>
<p><span>FONT 9, "</span><span>宋体</span><span>"</span></p>
<p><span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>ICON<span>&nbsp;&nbsp;&nbsp; </span>ICO_MAIN, -1, 10, 11, 18, 21</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CTEXT<span>&nbsp;&nbsp; </span>"</span><span>简单的对话框例子</span><span>\n </span><span>用</span><span>Win32ASM</span><span>编写</span><span>", -1, 36, 14, 70, 19</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>DEFPUSHBUTTON "</span><span>退出</span><span>(&amp;X)", IDOK, 58, 46, 50, 14</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CONTROL "", -1, "Static", SS_ETCHEDHORZ | WS_CHILD | WS_VISIBLE, 6, 39, 103, 1</span></p>
<p><span>}</span></p>
<p>&nbsp;</p>
<p><span>脚本文件中除了定义图标之外，另外还定义了一个</span><span>ID</span><span>为</span><span>1</span><span>的对话框，对话框中有</span><span>4</span><span>个子窗口控件，分别是图标、文本、按钮和一个横线，按钮的</span><span>ID</span><span>为</span><span>IDOK</span><span>，其他的子窗口控件由于是静态控件，不会向对话框过程发送命令，所以</span><span>ID</span><span>就设置为</span><span>-1</span><span>，这些控件的具体用法将在后面的内容中详细介绍。</span></p>
<p>&nbsp;</p>
<p><span>定义中还指定了一些可选属性，</span><span>STYLE</span><span>语句定义了对话框窗口的风格，</span><span>CAPTION</span><span>语句把标题栏定义为&#8220;对话框模板&#8221;，</span><span>FONT</span><span>语句指定了对话框使用的字体是大小为</span><span>9</span><span>的宋体。</span></p>
<p>&nbsp;</p>
<p><span>对话框的位置为（</span><span>50</span><span>，</span><span>50</span><span>），大小为宽</span><span>113</span><span>单位，高</span><span>64</span><span>单位，读者可能已经注意到：这个对话框的大小好像比宽</span><span>113</span><span>像素、高</span><span>64</span><span>像素的窗口要大，事实上的确如此，这也正是大小是&#8220;单位&#8221;而不是&#8220;像素&#8221;的原因。对话框的位置、大小以及所有子窗口控件的度量单位是根据系统字体的大小来决定的，横向（</span><span>x</span><span>坐标和宽度）每单位为系统字符平均宽度的</span><span>1/4</span><span>，纵向（</span><span>y</span><span>坐标和高度）每单位为字符平均高度的</span><span>1/8</span><span>，由于系统字体的字符高度大致为宽度的两倍，所以虽然这种计算方法有些费解，但横向和纵向的数值在视觉上还是成比例的，但和以&#8220;像素&#8221;为单位在数值上肯定是不同的。如果读者一定要知道这个值换算成像素后是多少，那么可以用</span><span>GetDialogBaseUnits</span><span>函数来获取系统字体的高度和宽度再进行计算。</span></p>
<p>&nbsp;</p>
<p><span>当一些英文版的软件在中文</span><span>Windows</span><span>上运行的时候，对话框中有些文本往往被砍掉了尾巴，原因就是这些程序是在英文</span><span>Windows</span><span>上调试的，文本框的尺寸是以英文</span><span>Windows</span><span>系统字符的大小来度量的，到了其他语言的</span><span>Windows</span><span>上后，系统字符的大小可能改变，对话框的大小也随着改变，结果就是原来刚好的宽度可能会变得不够，这也算是对话框尺寸度量方法的缺点吧！</span></p>
<p>&nbsp;</p>
<p><span>使用文本编辑器直接书写对话框脚本定义不是很直观，所以在创建对话框资源时最好使用可视化的资源编辑器，如</span><span>VC++</span><span>或</span><span>ResourceWorkshop</span><span>等。</span></p>
<p>&nbsp;</p>
<p><span>在子窗口控件的</span><span>ID</span><span>定义中有两个特殊的</span><span>ID</span><span>值——</span><span>IDOK</span><span>和</span><span>IDCANCEL</span><span>，在</span><span>Resource.h</span><span>中它们的值定义为</span><span>1</span><span>和</span><span>2</span><span>，</span><span>IDOK</span><span>是默认的&#8220;确定&#8221;</span><span>ID</span><span>，</span><span>IDCANCEL</span><span>是默认的&#8220;取消&#8221;</span><span>ID</span><span>。如果一个按钮的</span><span>ID</span><span>是</span><span>IDOK</span><span>，当焦点没有停留在其他按钮上的时候，在任何地方按下回车键就相当于按下了这个按钮，而按下</span><span>Esc</span><span>键的时候，就相当于按下了</span><span>ID</span><span>为</span><span>IDCANCEL</span><span>的按钮。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、</span><span>Tab</span><span>停留位和组</span></p>
<p><span>对话框中可以定义多个子窗口控件，有的了窗口控件可以拥有输入焦点（如按钮、文本框与组合框等），有些则不能（如图标与文本等），当对话框中有多个允许拥有输入焦点的子窗口控件时（有</span><span>WS_TABSTOP</span><span>风格），用户可以用</span><span>Tab</span><span>键将输入焦点切换到下一个有</span><span>WS_TABSTOP</span><span>风格的子窗口控件上，也可以用</span><span>Shift+Tab</span><span>键切换到上一个，</span><span>Tab</span><span>键切换的顺序就叫做</span><span>Tab</span><span>停留位。</span></p>
<p>&nbsp;</p>
<p><span>Tab</span><span>停留位并不是系统根据子窗口控件的坐标位置自动排列的，而是按照子窗口控件在资源脚本文件中的定义顺序来排列的，所以读者在定义的时候最好根据子窗口控件的位置适当排列语句的先后，以免按动</span><span>Tab</span><span>键切换的时候焦点上下左右无规则的地跳来跳去。如果使用可视化的资源编辑器，那么菜单中一般会有&#8220;</span><span>Tab</span><span>停留位&#8221;菜单项，在编辑完成后也要进到这个菜单项中设置一下，资源编辑器会根据设置调整</span><span>rc</span><span>文件中定义语句的先后顺序。</span></p>
<p>&nbsp;</p>
<p><span>对话框中往往有一些排列在一起的同类子窗口控件，如几个单选钮，几个单选钮之间的选中标记是互斥的，在对话框的其他地方可能又有一组互斥的单选钮用来代表其他功能，在对话框中规定所有的单选钮都是互斥的显然不现实，解决的方法就是将不同的子窗口控件&#8220;分组&#8221;，这就是&#8220;组&#8221;的含义。使用中可以选择一些子窗口控件定义</span><span>WS_GROUP</span><span>属性，两个有</span><span>WS_GROUP</span><span>属性的子窗口控件之间的所有子窗口控件同属同一组。</span></p>
<p>&nbsp;</p>
<p><span>使用对话框</span></p>
<p><span>使用对话框的代码分为创建部分和对话框过程两个部分，完整代码如下所示：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.386</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.model flat, stdcall</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>option casemap :none</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; Include </span><span>文件定义</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>windows.inc</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.lib</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.lib</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; Equ </span><span>等值定义</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>ICO_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1000h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>图标</span></p>
<p><span>DLG_MAIN<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>equ<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>对话框</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>数据段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.data?</span></p>
<p><span>hInstance<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>代码段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.code</span></p>
<p><span>_ProcDlgMain<span>&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp; </span>uses ebx edi esi hWnd, wMsg, wParam, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, wMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_CLOSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;EndDialog, hWnd, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>.elseif eax == WM_INITDIALOG</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;LoadIcon, hInstance, ICO_MAIN</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;SendMessage, hWnd, WM_SETICON, ICON_BIG, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif eax == WM_COMMAND</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, wParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == IDOK</span></p>
<p><span><span>&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; </span>invoke&nbsp;EndDialog, hWnd, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.else</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, FALSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, TRUE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>_ProcDlgMain<span>&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>start:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetModuleHandle, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>hInstance, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;DialogBoxParam, hInstance, DLG_MAIN, NULL, offset _ProcDlgMain, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;ExitProcess, NULL</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>end<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>start</span></p>
<p>&nbsp;</p>
<p><span>读者可以发现，相对于普通窗口的使用，对话框的使用显得特别简单，最明显的区别在于主程序中的一大堆代码不见了，换成了一个</span><span>DialogBoxParam</span><span>语句。</span></p>
<p>&nbsp;</p>
<p><span>1</span><span>、创建模态对话框</span></p>
<p><span>创建模态对话框的函数是</span><span>DialogBoxParam</span><span>，它的使用方法是：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>DialogBoxParam, hInstance, lpTemplateName, hWndParent, \</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>lpDialogFunc, dwInitParam</span></p>
<p><span>函数的各参数说明如下：</span></p>
<p><span>&#183;</span><span>hInstance</span><span>和</span><span>lpTemplateName</span><span>——函数从</span><span>hInstance</span><span>参数指定的模块中装入</span><span>lpTemplateName</span><span>参数指定的对话框资源，然后显示对话框窗口。例子程序中的</span><span>lpTemplateName</span><span>参数用的就是我们定义的</span><span>DLG_MAIN</span><span>。</span></p>
<p><span>&#183;</span><span>hWndParent</span><span>——对话框的父窗口，对话框关闭之前将无法切换到父窗口所属的其它窗口中，例子中用对话框做主窗口，所以父窗口句柄是</span><span>NULL</span><span>，在其他程序中使用时，这个参数设置为主窗口的句柄。</span></p>
<p><span>&#183;</span><span>lpDialogFunc</span><span>——指定了对话框过程的地址，例子程序中是</span><span>_ProcDlgMain</span><span>。</span></p>
<p><span>&#183;</span><span>dwInitParam</span><span>——当做</span><span>WM_INITDIALOG</span><span>消息的</span><span>lParam</span><span>传给对话框过程，读者可以用它来做自定义的用途。</span></p>
<p>&nbsp;</p>
<p><span>要结束模态对话框，必须在对话框过程的</span><span>WM_CLOSE</span><span>消息中使用</span><span>EndDialog</span><span>函数：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>EndDialog, hDlg, dwResult</span></p>
<p><span>不能使用通常的</span><span>DestroyWindow</span><span>函数，参数中的</span><span>hDlg</span><span>就是对话框窗口的句柄，</span><span>dwResult</span><span>参数是退出时的返回值，这个值最后由</span><span>DialogBoxParam</span><span>函数返回到主程序中。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、创建非模态对话框</span></p>
<p><span>创建非模态对话框的函数是</span><span>CreateDialogParam</span><span>，它的参数定义和</span><span>DialogBoxParam</span><span>一模一样：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>CreateDialogParam, hInstance, lpTemplateName, hWndParent, \</span></p>
<p><span><span>&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; </span>lpDialogFunc, dwInitParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hDlg, eax</span></p>
<p>&nbsp;</p>
<p><span>CreateDialogParam</span><span>和</span><span>DialogBoxParam</span><span>在使用中有几个不同点：</span></p>
<p><span>&#183;</span><span>CreateDialogParam</span><span>在创建对话框后，会根据对话框模板的风格是否定义了</span><span>WS_VISIBLE</span><span>来决定是否显示对话框窗口。如果定义了则显示，没有的话，则程序需要在以后自行调用</span><span>ShowWindow</span><span>来显示它；而</span><span>DialogBoxParam</span><span>函数不管是否定义了</span><span>WS_VISIBLE</span><span>风格都会显示对话框。</span></p>
<p><span>&#183;</span><span>CreateDialogParam</span><span>在建立对话框窗口后直接返回，返回值是对话框窗口的句柄；而</span><span>DialogBoxParam</span><span>要在对话框关闭后才返回，返回值是</span><span>EndDialog</span><span>中的</span><span>dwResult</span><span>参数。</span></p>
<p><span>&#183;在</span><span>CreateDialogParam</span><span>返回后，应用程序在自己的消息循环中获取对话框消息，所以如果要用非模态对话框做程序的主窗口，消息循环的代码还是要写的；而</span><span>DialogBoxParam</span><span>是使用</span><span>Windows</span><span>为它内建的消息循环。</span></p>
<p><span>&#183;关闭非模态对话框使用</span><span>DestroyWindow</span><span>函数，注意在这里不要用</span><span>EndDialog</span><span>函数。</span></p>
<p>&nbsp;</p>
<p><span>3</span><span>、对话框过程</span></p>
<p><span>Windows</span><span>在&#8220;对话框管理器&#8221;——也就是为对话框内建的窗口过程中处理对话框消息，在处理前会首先调用用户定义的对话框过程，程序可以在这里选择是否自行处理某些消息。读者在理解时可以把&#8220;对话框管理器&#8221;看成是对话框的&#8220;</span><span>DefWindowProc</span><span>&#8221;，凡是自己不想处理的消息都由它来处理。</span></p>
<p>&nbsp;</p>
<p><span>和窗口过程一样，对话框过程是一个&#8220;回调&#8221;子程序，它由程序定义，</span><span>Windows</span><span>来调用，模态对话框和非模态对话框的对话框过程是一样的。</span></p>
<p>&nbsp;</p>
<p><span>对话框过程和窗口过程的输入参数是一样的，也是：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DialogProc<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hwndDlg, uMsg, wParam, lParam</span></p>
<p><span>在程序里面一般编写对话框过程的分支结构如下：</span></p>
<p><span>_ProcDlgMain<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>proc<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>uses ebx edi esi hWnd, wMsg, wParam, lParam</span></p>
<p><span><span>&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; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, wMsg</span></p>
<p><span><span>&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; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_CLOSE</span></p>
<p><span><span>&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; </span>;</span><span>模态对话框用</span><span>EndDialog</span><span>关闭</span></p>
<span><br clear=all></span>
<p><span><span>&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; </span>;</span><span>非模态对话框用</span><span>DestroyWindow</span><span>关闭</span></p>
<p><span><span>&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; </span>.elseif<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_INITDIALOG</span></p>
<p><span><span>&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; </span>;</span><span>初始化代码</span></p>
<p><span><span>&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; </span>.elseif<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_COMMAND</span></p>
<p><span><span>&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; </span>;</span><span>子窗口控件发送的消息</span></p>
<p><span><span>&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; </span>;wParam</span><span>的低</span><span>16</span><span>位为子窗口控件</span><span>ID</span></p>
<p><span><span>&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; </span>.elseif<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_XXXX</span></p>
<p><span><span>&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; </span>;</span><span>处理其他需要处理的消息</span></p>
<p><span><span>&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; </span>.else</span></p>
<p><span><span>&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; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, FALSE</span></p>
<p><span><span>&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; </span>ret</span></p>
<p><span><span>&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; </span>.endif</span></p>
<p><span><span>&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; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax, TRUE</span></p>
<p><span><span>&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; </span>ret</span></p>
<p><span>_ProcDlgMain<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p>&nbsp;</p>
<p><span>注意对话框过程和普通的窗口过程在使用上有以下区别：</span></p>
<p><span>&#183;窗口过程对应于不同的消息有各种不同含义的返回值，而对话框过程返回</span><span>BOOL</span><span>类型的值，返回</span><span>TRUE</span><span>表示已经处理了某条消息，返回</span><span>FALSE</span><span>表示没有处理。&#8220;对话框管理器&#8221;代码会根据返回值决定是否继续处理某一条消息（唯一的例外是</span><span>WM_INITDIALOG</span><span>消息）。</span></p>
<p><span>&#183;对于不处理的消息，不需要调用</span><span>DefWindowProc</span><span>来处理，这事情由&#8220;对话框管理器&#8221;来做。</span></p>
<p>&nbsp;</p>
<p><span>&#8220;对话框管理器&#8221;不会把</span><span>WM_CREATE</span><span>消息转发给对话框过程，取而代之，它会以</span><span>WM_INITDIALOG</span><span>消息来调用对话框过程，程序可以在这里进行一些初始化的操作，</span><span>WM_INITDIALOG</span><span>消息的返回值有点特殊，如果程序想自行设置输入焦点，那么可以用</span><span>SetFocus</span><span>函数把输入焦点设置到需要的子窗口控件上，然后返回</span><span>FALSE</span><span>；如果返回</span><span>TRUE</span><span>的话，那么</span><span>Windows</span><span>会自动将输入焦点设置到第一个有</span><span>WS_TABSTOP</span><span>的子窗口控件上。</span></p>
<p>&nbsp;</p>
<p><span>对话框过程在</span><span>WM_COMMAND</span><span>消息中处理子窗口控件发送的命令，当用户在对话框中按下了按钮，输入文字或选择复选框等操作时，子窗口控件会向对话框过程发送</span><span>WM_COMMAND</span><span>消息，</span><span>wParam</span><span>是子窗口控件的</span><span>ID</span><span>，如例子程序中处理&#8220;退出&#8221;按钮的消息，在里面用</span><span>EndDialog</span><span>函数关闭对话框。</span></p>
<p>&nbsp;</p>
<p><span>对话框窗口的标题栏上默认没有定义图标，如果要像普通窗口一样显示一个图标，那么可以像例子程序中那样，在</span><span>WM_INIDIALOG</span><span>中用</span><span>WM_SETICON</span><span>消息来设置。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/125536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-09-01 16:57 <a href="http://www.cppblog.com/luqingfei/archive/2010/09/01/125536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--位图</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/31/125404.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Tue, 31 Aug 2010 07:19:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/31/125404.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/125404.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/31/125404.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/125404.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/125404.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>使用资源</span><span>—</span><span>位图</span></p>
<p>&nbsp;</p>
<p><span>位图（</span><span>Bitmap</span><span>）是</span><span>Windows</span><span>操作系统存储图像的方式，图像中的每个像素对应存储器中的一个或多个数据位，如单色位图每个像素对应</span><span>1</span><span>位，</span><span>16</span><span>色位图每个像素对应</span><span>4</span><span>位，</span><span>256</span><span>色为</span><span>8</span><span>位，全彩色为</span><span>24</span><span>位等，所有的像素数据按照一行行的顺序排列在存储器中，<strong>每个像素对应的位数称为颜色深度</strong>。</span></p>
<p>&nbsp;</p>
<p><span>位图的优越之处是操作的速度很快，计算机的屏幕显示是由硬件从视频缓冲区中的数据映射的，向视频缓冲区中拷贝数据就可以直接将图像显示在屏幕上，所以以位图的方式存储像素，显示图形的时间几乎就是向视频缓冲区拷贝数据的时间。</span></p>
<p>&nbsp;</p>
<p><span>位图的不便之处一是尺寸问题：由于位图是不压缩的，它占用的空间很大，一个</span><span>1024x768</span><span>像素</span><span>24</span><span>位色的图像的大小为</span><span>1024x768x3</span><span>字节，达</span><span>2.3MB</span><span>；二是位图的缩放问题，读者都知道矢量和位图之间的关系，矢量图形（现在网上最流行的</span><span>Flash</span><span>就是矢量格式的）可以无限制缩放而不变形，因为它是根据矢量实时计算出像素数据的，而位图缩放后要对原来的像素数据进行插值计算，不可避免地会有失真。</span></p>
<p>&nbsp;</p>
<p><span>在使用</span><span>Windows</span><span>的位图之前，必须搞清楚几个概念：<strong>位图、设备无关位图和位图文件</strong>。</span></p>
<p>&nbsp;</p>
<p><span>单纯意义上的&#8220;位图&#8221;指的就是存放在内存中、可以马上使用的位图，它的颜色深度总是对应当前设备（如屏幕或打印机等）的颜色深度，<strong>不和具体的设备对应，位图数据是没有意义的，因为无法知道要把数据中的多少位解释成一个像素</strong>。</span></p>
<p>&nbsp;</p>
<p><span>对于存放在磁盘上或别的地方的位图数据来说，它的颜色深度有可能和屏幕颜色深度不同，为了准确描述它的颜色信息，必须有像素数据的属性说明以及色彩表，在使用这个位图的时候，可以根据这些信息将像素数据转换到需要的颜色深度。色彩表和位图数据合在一起就叫做设备无关位图（</span><span>DIB</span><span>），因为它转换后可以用在不同颜色深度的设备上。</span><span>Windows</span><span>有函数专门用来处理</span><span>DIB</span><span>。使用</span><span>DIB</span><span>唯一的问题是当将高颜色数的</span><span>DIB</span><span>转换到低颜色数的设备上时，由于色彩只能被转换成设备所能表示的最相近的颜色，所以可能会有很大的颜色失真。</span></p>
<p>&nbsp;</p>
<p><span>DIB</span><span>可以存放在磁盘上的位图文件中，位图文件一般以</span><span>bmp</span><span>为扩展名，它的内容包括一个</span><span>bitmap</span><span>文件头和</span><span>DIB</span><span>数据，</span><span>bitmap</span><span>文件头可以用来验证整个文件的有效性。所以简单地讲，</span><span>DIB</span><span>是位图数据的超集，位图文件又是</span><span>DIB</span><span>的超集。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>支持的图形文件格式只有</span><span>bmp</span><span>，</span><span>ico</span><span>与</span><span>cur</span><span>等几种，可以广泛用在</span><span>GDI</span><span>操作中的只有</span><span>bmp</span><span>文件，其他格式文件如</span><span>jpg</span><span>与</span><span>tif</span><span>等都是不能直接应用的，要使用这些文件，必须在代码中将它们转换到位图格式以后才行，所以要编写一个仅支持</span><span>bmp</span><span>的图片浏览器是很简单的，而要支持其他格式麻烦就大了，仅</span><span>jpg</span><span>格式的解码就是个很复杂的问题！</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>在资源中定义位图</span></p>
<p><span>Windows</span><span>对</span><span>bmp</span><span>文件的支持有两种方法，一种是打开</span><span>bmp</span><span>文件读入</span><span>DIB</span><span>部分的数据，然后用函数将</span><span>DIB</span><span>数据转换到位图数据；另一种方法就是在资源文件中用和</span><span>ico</span><span>，</span><span>cur</span><span>文件类似的方法定义位图资源，然后在程序中装入后使用。</span></p>
<p>&nbsp;</p>
<p><span>在资源脚本文件中定义位图资源的语法是：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>位图</span><span>ID<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>BITMAP<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>[DISCARDABLE]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>位图文件名</span></p>
<p>&nbsp;</p>
<p><span>在程序中可以用</span><span>LoadBitmap</span><span>函数装入位图资源：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>LoadBitmap, hInstance, lpBitmapName</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hBitmap, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span>LoadBitmap</span><span>函数返回一个位图句柄，在程序退出的时候，位图句柄必须用</span><span>DeleteObject</span><span>函数释放。对位图资源的大部分操作涉及</span><span>GDI</span><span>的内容，以后再详细学习。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/125404.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-31 15:19 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/31/125404.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--图标和光标</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/31/125387.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Tue, 31 Aug 2010 03:55:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/31/125387.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/125387.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/31/125387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/125387.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/125387.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;使用资源—图标和光标&nbsp;图标和光标是图形资源，图标通常用做应用程序的&#8220;形象代表&#8221;出现在文件浏览器、运行窗口左上角或程序的快捷方式等所有代表文件的地方，为自己写的应用程序选一个合适的图标会使程序变得引人注目；而光标就是鼠标移动时屏幕上那个指示位置的东西，应用程序可以定义自己的光标，这样光标移到程序的客户区中就会变成需要的形状。&nbsp;...&nbsp;&nbsp;<a href='http://www.cppblog.com/luqingfei/archive/2010/08/31/125387.html'>阅读全文</a><img src ="http://www.cppblog.com/luqingfei/aggbug/125387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-31 11:55 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/31/125387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用资源--菜单和加速键</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/31/125352.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Tue, 31 Aug 2010 01:43:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/31/125352.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/125352.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/31/125352.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/125352.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/125352.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;使用资源 —— 菜单和加速键&nbsp;主菜单，顶层菜单弹出式菜单，子菜单右键弹出式菜单系统弹出式菜单&nbsp;菜单中的菜单项有好几种，从资源定义的角度来看，分隔用的横线也是一个菜单项，除横线外其他菜单项可以供用户选择，也可以设置为&#8220;禁止&#8221;或&#8220;灰化&#8221;状态暂时停用。&nbsp;菜单项上的圆点表示选中...&nbsp;&nbsp;<a href='http://www.cppblog.com/luqingfei/archive/2010/08/31/125352.html'>阅读全文</a><img src ="http://www.cppblog.com/luqingfei/aggbug/125352.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-31 09:43 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/31/125352.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--窗口程序实验--行为与消息之间的关系</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/23/124399.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Mon, 23 Aug 2010 05:11:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/23/124399.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/124399.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/23/124399.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/124399.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/124399.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;窗口程序实验--行为与消息之间的关系在这一节中，将通过不同的实验，进一步介绍窗口的运行。首先构造一个程序，在程序中将收到的消息查表翻译成文本以WM_XXX格式显示出来，并且将调用各个API函数的过程也显示出来，这样可以分析窗口的各种行为和消息之间的关系。&nbsp;一、MsgWindow程序为了把需要的内容显示出来，可以选择在客户区显示文本，但这样会引入新的消息，干...&nbsp;&nbsp;<a href='http://www.cppblog.com/luqingfei/archive/2010/08/23/124399.html'>阅读全文</a><img src ="http://www.cppblog.com/luqingfei/aggbug/124399.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-23 13:11 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/23/124399.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--窗口间的消息互发</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/18/123817.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 18 Aug 2010 05:04:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/18/123817.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/123817.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/18/123817.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/123817.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/123817.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>窗口间的消息互发</span></p>
<p>&nbsp;</p>
<p><span>在不同应用程序之间的窗口中可以互发消息，方法是通过</span><span>SendMessage</span><span>或者</span><span>PostMessage</span><span>函数，它们的用法如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke PostMessage, hWnd, Msg, wParam, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke SendMessage, hWnd, Msg, wParam, lParam</span></p>
<p>&nbsp;</p>
<p><span>对于不同的</span><span>Msg</span><span>，</span><span>wParam</span><span>和</span><span>lParam</span><span>的含义是不同的，如对于</span><span>WM_SETTEXT</span><span>是：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>wParam = 0;<span>&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; </span>//</span><span>未定义，必须为</span><span>0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>lParam = (LPARAM)(LPCTSTR)lpsz;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span>要设置的字符串地址</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>想一想就会发现一个问题：</span><span>Windows</span><span>中不同应用程序的地址空间是隔离的，全市程序</span><span>1</span><span>要用</span><span>SendMessage</span><span>调用程序</span><span>2</span><span>所属窗口的窗口过程，但程序</span><span>2</span><span>窗口过程的代码并不在程序</span><span>1</span><span>的地址空间中，那么</span><span>SendMessage</span><span>如何调用它呢？其实很简单，当程序</span><span>1</span><span>调用</span><span>SendMessage</span><span>函数的时候，</span><span>Windows</span><span>会先保存</span><span>wParam</span><span>和</span><span>lParam</span><span>参数并等待，等轮到程序</span><span>2</span><span>的时间片的时候再去调用它的窗口过程，并把保存的</span><span>wParam</span><span>和</span><span>lParam</span><span>参数发给它，等窗口过程返回的时候，</span><span>Windows</span><span>记下返回值并等待程序</span><span>1</span><span>，这样程序</span><span>1</span><span>看上去就像自己直接在调用程序</span><span>2</span><span>的窗口过程一样。</span></p>
<p>&nbsp;</p>
<p><span>但又一个问题出现了：</span><span>Windows</span><span>在做&#8220;牵线红娘&#8221;的时候传递了</span><span>wParam</span><span>和</span><span>lParam</span><span>以及返回值，如果参数指向一个字符串呢，比如说上面的</span><span>WM_SETTEXT</span><span>消息中的</span><span>lParam</span><span>指向一个字符串，假设程序</span><span>1</span><span>中</span><span>lParam</span><span>指向字符串的地址为</span><span>xxxxxxxx</span><span>，把这个地址传给程序</span><span>2</span><span>的时候，程序</span><span>2</span><span>不可能访问到程序</span><span>1</span><span>的地址空间，在程序</span><span>2</span><span>中</span><span>xxxxxxxx</span><span>指向的可能是其他内容，也可能是不可访问的，这又该如何处理呢？</span></p>
<p>&nbsp;</p>
<p><span>写一个源程序实验一下，用一个程序向另一个程序的窗口发送</span><span>WM_SETTEXT</span><span>消息，然后在另一个程序中将接收到的</span><span>WM_SETTEXT</span><span>消息的参数显示出来。先来打造接收程序，首先拷贝一份</span><span>FirstWindows</span><span>的代码，然后在窗口过程的分支中加上以下代码：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax&nbsp;== WM_SETTEXT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>wsprintf, addr szBuffer, addr szReceive, lParam, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>MessageBox, hWnd, offset szBuffer, addr szCaptionMain, MB_ok</span></p>
<p>&nbsp;</p>
<p><span>同时在数据段中加上下列定义：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>szCaptionMain<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp; </span>&#8216;Receive Messag&#8217;,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>szReceive<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp; </span>&#8216;Receive WM_SETTEXT message&#8217;,0dh,0ah</span></p>
<p><span><span>&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; </span>db<span>&nbsp;&nbsp;&nbsp; </span>&#8216;param: %08x&#8217;, 0dh, 0ah</span></p>
<p><span><span>&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; </span>db<span>&nbsp;&nbsp;&nbsp; </span>&#8216;text: &#8220;%s&#8221;, 0dh, 0ah, 0</span></p>
<p>&nbsp;</p>
<p><span>在这里，要提及</span><span>Win32 API</span><span>中一个很常用的函数</span><span>wsprintf</span><span>，这是一个字符串格式化函数，可以将数值按指定格式翻译成字符串，类似于</span><span>C</span><span>语言中的</span><span>printf</span><span>函数，它的原型是这样的：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int<span>&nbsp;&nbsp;&nbsp; </span>wsprintf (</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>LPTSTR<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>lpOut,<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span>输出缓冲地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>LPCTSTR<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>lpFmt<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span>格式化串地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;<span>&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; </span>//</span><span>变量列表</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>);</span></p>
<p><span>变量列表的数目由格式化字符串规定，</span><span>wsprintf</span><span>处理格式化字符串，遇到普通的字符则直接拷贝到输出，遇到</span><span>%</span><span>字符则代表有一个变量，</span><span>%</span><span>后面不同的字母表示不同的输出格式，如</span><span>%d</span><span>表示输出为整数，</span><span>%x</span><span>表示输出为</span><span>16</span><span>进制，</span><span>%s</span><span>表示输出字符串等。</span></p>
<p>&nbsp;</p>
<p><span>%</span><span>符号和表示格式的</span><span>d</span><span>，</span><span>x</span><span>和</span><span>s</span><span>等字母间可以用数字来指定输出时占用的位长，这时输出的位长不够时函数会用空格填齐。另外，表示位长的数字前可以加</span><span>0</span><span>来表示填齐时用&#8220;</span><st1:chmetcnv w:st="on" UnitName="&#8221;" SourceValue="0" HasSpace="False" Negative="False" NumberType="1" TCSC="0"><span>0</span><span>&#8221;</span></st1:chmetcnv><span>而非空格，如</span><span>%08x</span><span>表示输出为</span><span>8</span><span>位前面用</span><span>0</span><span>填齐的</span><span>16</span><span>进制数。</span></p>
<p>&nbsp;</p>
<p><span>wsprintf</span><span>是</span><span>Win32 API</span><span>中唯一一个参数数量不定的函数，使用</span><span>wsprintf</span><span>函数的时候，参数的数量取决于格式化字符串中用</span><span>%</span><span>号指定的数量，变量列表的数目和格式化串中的</span><span>%</span><span>格式一定要一一对应。这里</span><span>szReceive</span><span>中有两个</span><span>%</span><span>号定义，那么后面就要额外跟两个参数：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>wsprintf, addr szBuffer, addr szReceive, lParam, lParam</span></p>
<p>&nbsp;</p>
<p><span>这条语句将</span><span>lParam</span><span>的数值以及</span><span>lParam</span><span>的字符串按照</span><span>szReceive</span><span>格式化串定义的格式转换，并将结果存放到</span><span>szBuffer</span><span>中，然后程序将</span><span>szBuffer</span><span>中的内容在一个消息框中显示出来：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>MessageBox, hWnd, offset szBuffer, addr szCaptionMain, MB_OK</span></p>
<p><span>执行程序写好了，现在写一个发送程序，如下所示：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.386</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.model flat, stdcall</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>option casemap:none</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>windows.inc</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.lib</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;kernel32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.lib</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.data</span></p>
<p><span>hWnd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>szBuffer<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>256 dup (?)</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.const</span></p>
<p><span>szCaption<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'SendMessage',0</span></p>
<p><span>szStart<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'Press OK to start SendMessage, param:%08x!',0</span></p>
<p><span>szReturn<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'SendMessage returned!',0</span></p>
<p><span>szDestClass<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>db <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>'MyClass',0</span></p>
<p><span>szText<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'Text send to other windows',0</span></p>
<p><span>szNotFound<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'Receive Message Window no found!',0</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.code</span></p>
<p><span>start:</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;FindWindow,addr szDestClass, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov hWnd,eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;wsprintf, addr szBuffer, addr szStart, addr szText</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;MessageBox, NULL, offset szBuffer, offset szCaption, MB_OK</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;SendMessage, hWnd, WM_SETTEXT, 0, addr szText</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;MessageBox, NULL, offset szReturn, offset szCaption, MB_OK</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.else</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;MessageBox, NULL, offset szNotFound, offset szCaption, MB_OK</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;ExitProcess, NULL</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>在这个程序中首先用</span><span>FindWindow</span><span>函数找到接收窗口的窗口句柄，</span><span>FindWindow</span><span>函数的使用方法是：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>FindWindow, lpClassName, lpWindowName</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov hWin, eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p>&nbsp;</p>
<p><span>两个参数都指向字符串</span><span>lpClassName</span><span>指向需要寻找的窗口的窗口类，</span><span>lpWindowName</span><span>指向需要寻找窗口的窗口标题，如果目标窗口存在的话，函数的返回值是找到的窗口句柄，否则函数返回</span><span>0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>用接收窗口的窗口类当做参数寻找窗口，如果没有找到则显示&#8220;</span><span>Receive Message Window not found</span><span>&#8221;，找到的话则把&#8220;</span><span>Text send to other windows</span><span>&#8221;字符串的地址当做</span><span>WM_SETTEXT</span><span>消息的参数用</span><span>SendMessage</span><span>发送给接收窗口。</span></p>
<p>&nbsp;</p>
<p><span>好！，现在发送开始，首先执行</span><span>Receive.exe</span><span>，窗口出来了，然后执行</span><span>Send.exe</span><span>，屏幕上出现一个对话框：</span><span>Press OK to start SendMessage, param:00402072</span><span>，表示在</span><span>Send</span><span>程序中字符串的地址是</span><span>00402072h</span><span>，现在单击&#8220;确定&#8221;按钮执行</span><span>SendMessage</span><span>函数，单击后对话框消失，但接收程序显示出了一个对话框，内容为：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Receive WM_SETTEXT message</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>param: 0012fflc</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>text: &#8220;Text send to other windows&#8221;</span></p>
<p>&nbsp;</p>
<p><span>可见字符串是正确地传了过来，但地址却不是发送程序的</span><span>00402072h</span><span>，这是为何？</span></p>
<p><span>答案是</span><span>Window</span><span>做&#8220;红娘&#8221;做到底，它拷贝了</span><span>WM_SETTEXT</span><span>消息的</span><span>lParam</span><span>参数指向的字符串，并在接收程序的地址空间中开了一块内存放上这个字符串，然后把新的地址值当做</span><span>lParam</span><span>传给接收程序，毕竟在</span><span>WM_SETTEXT</span><span>消息中，</span><span>lParam</span><span>的数值是多少并不重要，重要的是它指向的字符串是否正确。</span></p>
<p>&nbsp;</p>
<p><span>最后，单击接收程序中的&#8220;确定&#8221;按钮，发送程序马上弹出一个消息框并显示：</span><span>SendMessage returned</span><span>，这是</span><span>SendMessage</span><span>函数告诉我们：我回来了！</span></p>
<p>&nbsp;</p>
<p><span>其实</span><span>Windows</span><span>在处理</span><span>SendMessage</span><span>和</span><span>PostMessage</span><span>的时候要检查消息的类型，并对不同的消息做不同的处理，当消息的参数是一个指针的时候，</span><span>Windows</span><span>要把指针指向的内容复制到缓冲区，以便在两个程序的地址空间中传递。</span></p>
<p>&nbsp;</p>
<p><span>注意：在用户自定义的消息中（</span><span>WM_USER</span><span>等）不要在消息参数中传递指针，这只会引发非法访问内存，因为</span><span>Windows</span><span>不知道用户的意图，它只会把</span><span>lParam</span><span>和</span><span>wParam</span><span>当两个普通的数值传递，而不会帮用户把指针指向的内容复制到一个缓冲区中。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/123817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-18 13:04 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/18/123817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--分析窗口程序</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/17/123678.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Tue, 17 Aug 2010 03:49:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/17/123678.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/123678.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/17/123678.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/123678.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/123678.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;分析窗口程序&nbsp;了解了消息驱动体系的工作流程以后，让我们来分析如何用Win32汇编实现这一切。&nbsp;模块和句柄模块的概念一个模块代表的是一个运行中的exe文件或dll文件，用来代表这个文件中所有的代码和资源，磁盘上的文件不是模块，装入内存后运行时就叫做模块。&nbsp;一个应用程序调用其他DLL中的API时，这些DLL文件被装入内存，就...&nbsp;&nbsp;<a href='http://www.cppblog.com/luqingfei/archive/2010/08/17/123678.html'>阅读全文</a><img src ="http://www.cppblog.com/luqingfei/aggbug/123678.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-17 11:49 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/17/123678.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--开始了解窗口程序</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/13/123286.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Fri, 13 Aug 2010 01:53:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/13/123286.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/123286.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/13/123286.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/123286.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/123286.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>窗口是什么？大家每天在使用</span><span>Windows</span><span>，屏幕上的一个个方块就是一个个窗口！那么，窗口为什么是这个样子呢？窗口就是程序吗？</span></p>
<p>&nbsp;</p>
<p><span>回想</span><span>DOS</span><span>时代的计算屏幕，在</span><span>1990</span><span>年</span><span>Windows 3.0</span><span>推出之前，计算机的屏幕一直使用文本模式，黑洞洞的底色上漂浮着白色的小字。对</span><span>DOS</span><span>程序来说，屏幕是唯一的，上面有光标表示输入字符的位置，程序运行后往屏幕输出一些信息，退出时输出的信息就留在了屏幕上，然后是第二个程序重复这个过程，当屏幕被写满的时候，整个屏幕上卷一行，最上面一行被去掉，然后程序在最底下新空出来的一行上继续输出。</span></p>
<p>&nbsp;</p>
<p><span>对于一个单任务的操作系统，这种方式是很合理的，因为平时使用传真机或打字机就是用上卷的方式来容纳新的内容的。但是如果是多任务呢？两个程序同时往屏幕上写东西或者两个人同时往打字机上打字，那么谁都看不懂混在一起的是什么。</span><span>DOS</span><span>下的</span><span>TSR</span><span>（内存驻留）程序是多个程序同时使用一个屏幕的例子，但实质上这并不是多任务，而是</span><span>TSR</span><span>将别的程序暂时挂起，挂起的程序不可能在</span><span>TSR</span><span>执行期间再向屏幕输出内容，</span><span>TSR</span><span>在输出自己的内容之前必须保存屏幕上显示的内容，并在退出的时候把屏幕恢复原来的样子，否则挂起的程序并不知道屏幕已经被改变，在这个过程中，</span><span>DOS</span><span>不会去干预中间发生的一切。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>是多任务的操作系统，可以同时运行多个程序，同样，各个程序在屏幕上的显示不能互相干扰，而且，多个程序可以看成是&#8220;同时&#8221;运行的，在后台的程序也可能随时向屏幕输出内容，这中间的高度是由</span><span>Windows</span><span>完成的。</span><span>Windows</span><span>采用的方法是给程序一块矩形的屏幕空间，这就是窗口。应用程序通过</span><span>Windows</span><span>向属于自己的窗口显示信息，</span><span>Windows</span><span>判断该窗口是不是被别的窗口挡住，并把没有挡住的部分输出到屏幕上，这样屏幕上显示的东西就不会互相覆盖而乱套。对于应用程序来说，它只需认为窗口就是自己拥有的显示空间就可以了。</span></p>
<p>&nbsp;</p>
<p><span>窗口和程序的关系</span></p>
<p><span>既然不同窗口的内容就是不同程序的输出，那么一个窗口就是一个程序吗？反过来，一个程序就是一个窗口吗？</span></p>
<p><span>答案是否定的，一个窗口不一定就是一个程序，它可能只是一个程序的一部分。一个程序可以建立多个顶层窗口，如</span><span>Windows</span><span>的桌面和任务栏都是顶层窗口，但它们都属于&#8220;文件管理器&#8221;进程，所以并不是一个窗口就是一个程序的代表。</span><span>Windows</span><span>的窗口采用层次结构，一个窗口中可以建立多个子窗口，如窗口中的状态栏，工具栏，对话框中的按钮，文本输入框与复选框等都是子窗口。子窗口中还可以建立下一级子窗口，如</span><span>Word</span><span>工具栏上的字体选择框。</span></p>
<p>&nbsp;</p>
<p><span>反过来，运行的程序并非一定就是窗口，比如悄悄在后台运行的木马程序就不会显示一个窗口向用户报告它在干什么。在</span><span>Windows NT</span><span>下用&#8220;任务管理器&#8221;查看，进程的数量比屏幕上的窗口多得多，意味着很多的运行程序并没有显示窗口。如果一个程序不想和用户交互，它可以选择不建立窗口。</span></p>
<p>&nbsp;</p>
<p><span>所以本章的标题&#8220;第一个窗口程序&#8221;，指学习编写第一个以标准的窗口为界面的程序，而不是泛指</span><span>Windows</span><span>程序。如果要写的</span><span>Win32</span><span>程序不是以窗口为界面的，如控制台程序等，就不一定采用本章中提及的以消息驱动的程序结构。</span></p>
<p>&nbsp;</p>
<p><span>虽然以窗口为界面的程序并不是所有</span><span>Windows</span><span>程序的必须选择，但绝大部分的应用程序是以这种方式出现的，从操作系统的名称&#8220;</span><span>Windows</span><span>&#8221;就可以看出这一点，了解窗口程序就是相当于在了解</span><span>Windows</span><span>工作方式的基础。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>窗口界面</span></p>
<p><span>大部分的窗口看上去都是大同小异。</span></p>
<p><span>典型的窗口，即</span><span>Windows</span><span>附带的写字板程序窗口。</span></p>
<p>&nbsp;</p>
<p><span>窗口一般由屏幕上的矩形区域组成，不同的窗口可能包括一些相同的组成部分，如标题栏、菜单、工具栏、边框和状态栏等，每个部分都有自己固定的行为模式：</span></p>
<p><span>&#183;窗口边框：窗口的外沿就是窗口边框，用鼠标按住边框并拖动可以调整窗口的大小。</span></p>
<p><span>&#183;标题栏：窗口的最上面是标题栏，用鼠标按住标题栏拖动可以移动窗口，双击标题栏则将窗口最大化或从最大化的状态恢复。通过标题栏的颜色可以区分窗口是不是活动窗口，同时标题栏列出了应用程序的名称。</span></p>
<p><span>&#183;菜单：标题栏下面是菜单，单击菜单会弹出各种功能选择。</span></p>
<p><span>&#183;工具栏：菜单的下面是工具栏，工具栏上用图标的方式列出最常用的功能，相当于菜单的快捷方式。</span></p>
<p><span>&#183;图标和最小化、最大化与关闭按钮：图标位于标题栏的左边，三个控制按钮则位于标题栏的右边。单击图标会弹出一个系统菜单，双击图标则相当于按下了关闭按钮。最小化、最大化按钮用来控制窗口的大小。</span></p>
<p><span>&#183;状态栏：位于窗口的最下面，用来显示一些状态信息。</span></p>
<p><span>&#183;客户区：窗口中间用来工作或输出的区域，叫做窗口的客户区，把窗口看做是一张白纸的话，客户区就是白纸中真正用来写东西的区域，程序在这里和用户进行交互。</span></p>
<p><span>&#183;滚动条：如果客户区太小不足以显示全部内容，右边或底部可能还有滚动条，拖动它可以滚动窗口的客户区，以便看到其他的内容。</span></p>
<p>&nbsp;</p>
<p><span>虽然大部分的窗口看上去都差不多，但并不是每个窗口都有这些东西，也许有的窗口就没有图标和最小化、最大化框，有的没有工具栏或状态栏，有的没有标题栏，而有的就干脆是个奇怪的形状，如</span><span>Office</span><span>帮助中助手，那些小狗小猫都些不折不扣的窗口，</span><span>Windows</span><span>的桌面和桌面下面的任务栏也都是窗口，就连屏幕保护的黑屏幕也是一个大小为整个屏幕、没有标题栏和边框的窗口！</span></p>
<p>&nbsp;</p>
<p><span>一致的窗口形状和行为模式为</span><span>Windows</span><span>用户提供了一致的用户界面，几乎所有的窗口程序都在菜单的第一栏设置有关文件的操作和退出功能、最后一栏设置程序的帮助，相同的功能在工具栏上的图标也是大同小蒸发量的，用户可以不再像在</span><span>DOS</span><span>下那样，对不同的程序需要学习不同的界面，用户自从学会使用第一个软件起，就基本学会了所有</span><span>Windows</span><span>软件的使用模式，而且可以通过相似的菜单、工具栏等来发掘程序的新功能。窗口的菜单和客户区则是最个性化的东西，菜单随程序的不同而不同，而客户区则是窗口程序的输出区域，不同的程序在客户区内显示了不同的内容。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>窗口程序是怎么工作的</span></p>
<p><span>窗口程序的运行模式</span></p>
<p><span>对程序员来说，要了解的不仅是用户可以看到的部分，还必须了解窗口底下的东西，了解用怎样的程序结构来实现窗口的行为模式。</span></p>
<p>&nbsp;</p>
<p><span>DOS</span><span>程序员熟悉的是顺序化的、按过程驱动的程序设计方法。程序有明显的开始、明显的过程和明显的结束，由程序运行的阶段来决定用户该做什么。而窗口程序是事件驱动的（再次提醒：这里是&#8220;窗口程序&#8221;，而不是&#8220;</span><span>Windows</span><span>程序&#8221;，因为和窗口有关的程序者事件驱动的，其他的</span><span>Windows</span><span>程序可能并不这样工作，如控制台程序的结构还是同</span><span>DOS</span><span>程序一样是顺序化的，但与窗口相关的</span><span>Windows</span><span>程序占了绝大多数，所以大部分的书籍中讲到</span><span>Windows</span><span>程序就认为是事件驱动的程序），用户可能随时发出各种消息，如操作的过程中觉得窗口不够大了，就马上会拖动边框，程序必须马上调整客户区的内容以适应新的窗口大小；用户觉得想先干别的事情，可能会把窗口最小化，关闭按钮也有可能随时被按下，这意味着程序要随时可以处理退出的请求。如果非要规定干活的时候不能移动窗口与调整大小，那么这些窗口就会呆在桌面上一动不动。</span></p>
<p>&nbsp;</p>
<p><span>窗口程序在结构上和</span><span>DOS</span><span>程序有很大的不同，窗口程序实现大部分功能的代码应该呆在同一个模块——消息处理模块，这个模块可以随时应付所有类型的消息，只有这样才能随时响应用户的各种操作。</span></p>
<p>&nbsp;</p>
<p><span>下面是地地道道的</span><span>Win32</span><span>汇编窗口程序：</span></p>
<p><span>FirstWindow</span><span>源代码。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.386</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.model flat,stdcall</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>option casemap:none</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; Include </span><span>文件定义</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>windows.inc</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>gdi32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>gdi32.lib</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>user32.lib</span></p>
<p><span>include<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.inc</span></p>
<p><span>includelib<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>kernel32.lib</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>数据段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.data?</span></p>
<p><span>hInstance<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p><span>hWinMain<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>?</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.const</span></p>
<p><span>szClassName<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'MyClass',0</span></p>
<p><span>szCaptionMain<span>&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'My first Window!',0</span></p>
<p><span>szText<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>'Win32 Assembly, Simple and powerful!',0</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>代码段</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.code</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>; </span><span>窗口过程</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>_ProcWinMain<span>&nbsp;&nbsp;&nbsp; </span>proc uses ebx edi esi, hWnd, uMsg, wParam, lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local @stPs:PAINTSTRUCT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local @stRect:RECT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local @hDc</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov eax,uMsg</span></p>
<p><span>;*************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>.if<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax == WM_PAINT</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>BeginPaint,hWnd,addr @stPs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@hDc,eax</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;GetClientRect,hWnd,addr @stRect</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke <span>&nbsp;&nbsp; </span>DrawText,@hDc,addr szText,-1, \</span></p>
<p><span><span>&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; </span>addr @stRect, \</span></p>
<p><span><span>&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; </span>DT_SINGLELINE or DT_CENTER or DT_VCENTER</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>EndPaint, hWnd, addr @stPs</span></p>
<p><span>;***************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.elseif eax == WM_CLOSE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>DestroyWindow,hWinMain</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>PostQuitMessage,NULL</span></p>
<p><span>;***************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.else</span></p>
<p><span><span>&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>DefWindowProc,hWnd,uMsg,wParam,lParam</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endif</span></p>
<p><span>;***************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>xor<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>eax,eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>_ProcWinMain<span>&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>_WinMain<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>proc</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@stWndClass:WNDCLASSEX</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>local<span>&nbsp;&nbsp; </span>@stMsg:MSG</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>GetModuleHandle,NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>hInstance,eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>RtlZeroMemory,addr @stWndClass,sizeof @stWndClass</span></p>
<p><span>;**************************************************************************</span></p>
<p><span>; </span><span>注册窗口类</span></p>
<p><span>;**************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;</span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>LoadCursor,0,IDC_ARROW</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.hCursor,eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push<span>&nbsp;&nbsp;&nbsp; </span>hInstance</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.hInstance</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.cbSize, sizeof WNDCLASSEX</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.style, CS_HREDRAW or CS_VREDRAW</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.lpfnWndProc, offset _ProcWinMain</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.hbrBackground,COLOR_WINDOW + 1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>@stWndClass.lpszClassName, offset szClassName</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>RegisterClassEx, addr @stWndClass</span></p>
<p><span>;***************************************************************************</span></p>
<p><span>; </span><span>建立并显示窗口</span></p>
<p><span>;***************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>CreateWindowEx, WS_EX_CLIENTEDGE, \</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>offset szClassName, offset szCaptionMain, \</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>WS_OVERLAPPEDWINDOW, \</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>100, 100, 600, 400, \</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>NULL, NULL, hInstance, NULL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>hWinMain,eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ShowWindow,hWinMain,SW_SHOWNORMAL</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>UpdateWindow,hWinMain</span></p>
<p><span>;**************************************************************************</span></p>
<p><span>; </span><span>消息循环</span></p>
<p><span>;**************************************************************************</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.while&nbsp;TRUE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>GetMessage, addr @stMsg, NULL, 0, 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.break<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>.if eax == 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>TranslateMessage, addr @stMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>DispatchMessage, addr @stMsg</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.endw</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>_WinMain<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>endp</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span>start:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call<span>&nbsp;&nbsp;&nbsp; </span>_WinMain</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>invoke&nbsp;ExitProcess, NULL</span></p>
<p><span>;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>end start</span></p>
<p>&nbsp;</p>
<p><span>编译运行后，窗口出来了，对于这个窗口，用户可以拖动边框去改变大小、按标题栏上的按钮来最大化和最小化，当光标到边框的时候，会自动变成箭头，总之，这个窗口包括了一个典型窗口的所有特征。</span></p>
<p>&nbsp;</p>
<p><span>接下来开始分析源代码，看了这三页多的源代码，第一个感觉是什么？是不是想撤退了？</span><span>90%</span><span>的人有同样的感觉，别急，过了这一关，</span><span>Win32</span><span>汇编的入门就成功了一半了，所以千万要挺住！有个振奋人心的消息是，这个程序是大部分窗口程序的模板，以后要写一个新的程序，把它拷贝过来再往中间添砖加瓦就是了，功夫一点都不白费。</span></p>
<p>&nbsp;</p>
<p><span>先静下心来分析一下程序的结构，首先是注释，模式定义，</span><span>include</span><span>，</span><span>.data</span><span>数据段，</span><span>.code</span><span>代码段。</span></p>
<p>&nbsp;</p>
<p><span>程序入口是</span><span>start</span><span>，然后执行了一下</span><span>_WinMain</span><span>子程序，完成后就是程序退出的函数</span><span>ExitProcess</span><span>，再看</span><span>_WinMain</span><span>的结构，前面是顺序下来的几个</span><span>API</span><span>调用：</span></p>
<p><span>GetMoudleHandle </span><span><span>&#224;</span></span><span> RelZeroMemory </span><span><span>&#224;</span></span><span> LoadCursor </span><span><span>&#224;</span></span><span> RegisterClassEx </span><span><span>&#224;</span></span><span> CreateWindowEx </span><span><span>&#224;</span></span><span> ShowWindow </span><span><span>&#224;</span></span><span> UpdateWindow</span></p>
<p>&nbsp;</p>
<p><span>从名称上就能看出它们的用途，很明显，窗口是在</span><span>CreateWindowEx</span><span>处建立的，</span><span>ShowWindow</span><span>则是把窗口显示在屏幕上，这些代码是窗口的建立过程。</span></p>
<p>&nbsp;</p>
<p><span>接下来，就是一个由</span><span>3</span><span>个</span><span>API</span><span>组成的循环了：</span></p>
<p><span>GetMessage </span><span><span>&#224;</span></span><span> TranslateMessage </span><span><span>&#224;</span></span><span> DispatchMessage</span></p>
<p>&nbsp;</p>
<p><span>很明显，这是和消息有关的循环，因为</span><span>API</span><span>名称中都带有</span><span>Message</span><span>字样，如果退出这个循环，程序也就结束了，这个循环叫做消息循环。</span></p>
<p>&nbsp;</p>
<p><span>设置</span><span>_WinMain</span><span>子程序并不是必须的，可以把</span><span>_WinMain</span><span>的所有代码放到主程序中，没有任何影响，之所以这样只是为将这里使用的变量定义成局部变量，这样可以方便移植。</span></p>
<p>&nbsp;</p>
<p><span>看了程序的流程，似乎没有什么地方涉及窗口的行为，如改变大小和移动位置的处理等。</span></p>
<p><span>再看源程序，除了</span><span>_WinMain</span><span>，还有一个子程序</span><span>_ProcWinMain</span><span>，但除了在</span><span>WNDCLASSEX</span><span>结构的赋值中提到过它，好像就没有什么地方要用到这个子程序，起码在自己编写的源代码中没有任何一个地方调用过它。</span></p>
<p>&nbsp;</p>
<p><span>再看</span><span>ProWinMain</span><span>，它是一个分支结构处理的子程序，功能是把参数</span><span>uMsg</span><span>取出来，根据不同的</span><span>uMsg</span><span>执行不同的代码，完了以后就退出了，中间也没有任何东西和主程序有关联。</span></p>
<p>&nbsp;</p>
<p><span>第一个窗口程序就是由这么两个似乎是风马牛不相及的部分组成的，但它确实能工作，对于写惯了</span><span>DOS</span><span>汇编的程序员来说，这似乎不可理解。下面来看看这么一个陌生而奇怪的程序是如何工作的。</span></p>
<p>&nbsp;</p>
<p><span>窗口程序的运行过程</span></p>
<p><span>在屏幕上显示一个窗口的过程一般有以下步骤，这就是主程序的结构流程：</span></p>
<p><span>1</span><span>）得到应用程序的句柄（</span><span>GetMoudleHandle</span><span>）。</span></p>
<p><span>2</span><span>）注册窗口类（</span><span>RegisterClassEx</span><span>）。在注册之前，要先填写</span><span>RegisterClassEx</span><span>的参数</span><span>WNDCLASSEX</span><span>结构。</span></p>
<p><span>3</span><span>）建立窗口（</span><span>CreateWindowEx</span><span>）。</span></p>
<p><span>4</span><span>）显示窗口（</span><span>ShowWindow</span><span>）。</span></p>
<p><span>5</span><span>）刷新窗口客户区（</span><span>UpdateWindow</span><span>）。</span></p>
<p><span>6</span><span>）进入无限的消息获取和处理的循环。首先获取消息（</span><span>GetMessage</span><span>），如果有消息到达，则将消息分派到回调函数处理（</span><span>DispatchMessage</span><span>），如果消息是</span><span>WM_QUIT</span><span>，则退出循环。</span></p>
<p>&nbsp;</p>
<p><span>程序的另一半</span><span>_ProcWinMain</span><span>子程序是用来处理消息的，它就是窗口的回调函数（</span><span>CallBack</span><span>），也叫做窗口过程，之所以是回调函数是因为它是由</span><span>Windows</span><span>而不是我们自己调用的，我们调用</span><span>DispatchMessage</span><span>，而</span><span>DispatchMessage</span><span>再回过来调用窗口过程。</span></p>
<p>&nbsp;</p>
<p><span>所有的用户操作都是通过消息来传给应用程序的，如用户按键，鼠标移动，选择了菜单和拖动了窗口等，应用程序中由窗口过程接收消息并处理，在例子程序中就是</span><span>_ProcWinMain</span><span>。窗口过程构造了一个分支结构，对应不同的消息执行不同的代码，所以一个应用程序中几乎所有的功能代码都集中在窗口过程里。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>在系统内部有一个系统消息队列，当输入设备有所动作的时候，如用户按动了键盘、移动了鼠标，按下或放开了鼠标等，</span><span>Windows</span><span>都会产生相应的记录放在系统消息队列里，每个记录中包含消息的类型、发生的位置（如鼠标在什么坐标移动）和发生的时间等信息。</span></p>
<p>&nbsp;</p>
<p><span>同时，</span><span>Windows</span><span>为每个程序（严格地说是每个线程）维护一个消息队列，</span><span>Windows</span><span>检查系统消息队列里消息的发生位置，当位置位于某个应用程序的窗口范围内的时候，就把这个消息派送到应用程序的消息队列里。</span></p>
<p>&nbsp;</p>
<p><span>当应用程序还没有来取消息的时候，消息就暂时保留在消息队里，当程序中的消息循环执行到</span><span>GetMessage</span><span>的时候，控制权转移到</span><span>GetMessage</span><span>所在的</span><span>USER32.DLL</span><span>中，</span><span>USER32.DLL</span><span>从程序消息队列中取出一条消息，然后把这条消息返回应用程序。</span></p>
<p>&nbsp;</p>
<p><span>应用程序可以对这条消息进行预处理，如可以用</span><span>TranslateMessage</span><span>把基于键盘扫描码的按键消息转换成基于</span><span>ASCII</span><span>码的键盘消息，以后也会用到</span><span>TranslateAccelerator</span><span>把键盘快捷键转换成命令消息，但这个步骤不是必需的。</span></p>
<p>&nbsp;</p>
<p><span>然后应用程序将处理这个消息，但方法不是自己直接调用窗口过程来完成，而是通过</span><span>DispatchMessage</span><span>间接调用窗口过程，</span><span>Dispatch</span><span>的英文含义是&#8220;分派&#8221;，之所以是&#8220;分派&#8221;，是因为一个程序可能建有不止一个窗口，不同的窗口消息必须分派给相应的窗口过程。当控制权转移到</span><span>USER32.DLL</span><span>中的</span><span>DispatchMessage</span><span>时，</span><span>DispatchMessage</span><span>找出消息对应窗口的窗口过程，然后把消息的具体信息当做参数来调用它，窗口过程根据消息找到对应的分支去处理，然后返回，这时控制权回到</span><span>DispatchMessage</span><span>，最后</span><span>DispatchMessage</span><span>函数返回应用程序。这样，一个循环就结束了，程序又开始新一轮的</span><span>GetMessage</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>窗口程序</span> <span><span>&#224;</span></span><span> GetMessage(USER32.DLL) &#8211;&gt; TranslateMessage(USER32.DLL) &#8211;&gt; DispatchMessage(USER32.DLL) </span><span><span>&#224;</span></span><span> </span><span>窗口过程</span> <span><span>&#224;</span></span><span> USER32.DLL </span><span><span>&#224;</span></span><span> </span><span>窗口程序</span></p>
<p>&nbsp;</p>
<p><span>为什么要由</span><span>Windows</span><span>来调用窗口过程，程序取了消自以后自己处理不是更简便吗？事实上并非如此，如果程序自己处理消息的&#8220;分派&#8221;，就必须自己维护本程序所属窗口的列表，当程序建立的窗口不止一个的时候，这个工作就变得复杂起来；另一个原因是：别的程序也可能用</span><span>SendMessage</span><span>通过</span><span>Windows</span><span>直接调用你的窗口过程；第三个原因：</span><span>Windows</span><span>并不是把所有的消息都放进消息队列，有的消息是直接调用窗口过程处理的，如</span><span>WM_SETCURSOR</span><span>等实时性很强的消息，所以窗口过程必须开放给</span><span>Windows</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>应用程序之间也可以互发消息，</span><span>PostMessage</span><span>是把一个消息放到其他程序的消息队列中，目标程序收到了这个条消息就把它放入该程序的消息队列去处理；而</span><span>SendMessage</span><span>则越过消息队列直接调用目标程序的窗口过程，窗口过程返回以后才从</span><span>SendMessage</span><span>返回。</span></p>
<p>&nbsp;</p>
<p><span>窗口过程是由</span><span>Windows</span><span>回调的，</span><span>Windows</span><span>又是怎么知道往哪里回调呢？答案是我们在调用</span><span>RegisterClassEx</span><span>函数的时候告诉了</span><span>Windows</span><span>。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/123286.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-13 09:53 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/13/123286.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--使用MASM</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/11/123078.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 11 Aug 2010 08:49:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/11/123078.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/123078.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/11/123078.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/123078.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/123078.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;使用MASM&nbsp;Win32汇编源程序的结构任何种类的语言，总是有基本的源程序结构规范。下面以经典的Hello World程序为例，展示一个C语言、DOS汇编、Win32汇编三种写法。同学位好好体会一下。如果没有汇编基础，建议看一下王爽老师的《汇编语言》这本书。&nbsp;C语言中的HelloWorld程序：#include &lt;stdio...&nbsp;&nbsp;<a href='http://www.cppblog.com/luqingfei/archive/2010/08/11/123078.html'>阅读全文</a><img src ="http://www.cppblog.com/luqingfei/aggbug/123078.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-11 16:49 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/11/123078.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--准备编程环境</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/06/122457.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Fri, 06 Aug 2010 08:25:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/06/122457.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122457.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/06/122457.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122457.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122457.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>准备编程环境</span></p>
<p>&nbsp;</p>
<p><span>Win32</span><span>可执行文件的开发过程</span></p>
<p><span>在</span><span>DOS</span><span>下，生成一个可执行文件的步骤比较简单，用编译器将源程序编译为</span><span>obj</span><span>文件，再用链接器将</span><span>obj</span><span>文件链接成</span><span>exe</span><span>文件。</span></p>
<p>&nbsp;</p>
<p><span>DOS</span><span>可执行文件中的内容基本上是由源程序中所写的代码和数据定义转换而来的。</span></p>
<p><span>唯一的例外是带覆盖部分的</span><span>exe</span><span>文件，它在基本的</span><span>exe</span><span>文件的附加了一些自定义的数据，可执行部分的长度由文件头偏移</span><span>0002h</span><span>和</span><span>0004h</span><span>中的长度给出，后面就是附加上去的数据。这样，即使一个带覆盖的</span><span>exe</span><span>文件大小远远超过</span><span>640KB</span><span>，在</span><span>DOS</span><span>下也能运行。因为操作系统只装入真正的可执行部分，然后由程序自己去读取覆盖部分的数据。一些打包软件生成的奇大无比的自解压包就采用这种结构，可执行部分是解包代码，覆盖部分是被压缩的数据。</span><span>DOS</span><span>对可执行文件覆盖部分的数据格式并没有规定，它是程序员按自己的方式组织的。如果程序员愿意，也可以把这些数据单独放在另外一个文件中。</span></p>
<p>&nbsp;</p>
<p><span>Win32</span><span>可执行文件叫做</span><strong><span>PE</span></strong><strong><span>文件</span></strong><span>。</span><span>PE</span><span>文件的基本结构和</span><span>DOS</span><span>可执行文件有很大的不同。它把程序中的不同部分分成各种节区（</span><span>Section</span><span>），其中可以有一个节区是放置各种资源的，如菜单、对话框、位图、光标、图标和声音等。<strong>虽然可以把资源部分理解成类似</strong></span><strong><span>DOS</span></strong><strong><span>可执行文件中的覆盖部分，但资源是</span><span>Win32</span></strong><strong><span>可执行文件的标准组成部分，而且是非常重要的组成部分。所以和</span><span>DOS</span></strong><strong><span>软件的开发过程相比，</span><span>Win32</span></strong><strong><span>软件的开发中多了一个创建资源文件的步骤。</span></strong></p>
<p>&nbsp;</p>
<p><span>Win32</span><span>汇编开发软件的流程，分为创建代码和创建资源两部分。</span></p>
<p>&nbsp;</p>
<p><span>代码部分的开发工作和</span><span>DOS</span><span>下写代码的步骤是一样的。</span></p>
<p>&nbsp;</p>
<p><span>程序员用文本逻辑器书写汇编源代码（</span><span>*.asm</span><span>文件），这和</span><span>C</span><span>源程序类似。</span><span>asm</span><span>文件中也可以用</span><span>include</span><span>语句包含数据定义和函数声明的头文件。</span><span>Win32</span><span>汇编的头文件一般用</span><span>inc</span><span>作扩展名。</span><span>asm</span><span>文件经汇编编译器编译成以</span><span>obj</span><span>为扩展名的目标文件。</span></p>
<p>&nbsp;</p>
<p><span>资源文件中可以包括对话框、快捷键、菜单、字符串、版本信息和一些图形资源等内容。资源文件的源文件是一种类似脚本的文本文件。其中用不同的语法定义了不同类型的资源。资源脚本文件的扩展名一般为</span><span>rc</span><span>，经过资源编译器编译成资源文件</span><span>*.res</span><span>。资源脚本文件同样用到很多预定义值，所以软件包中一般也有资源头文件可供源文件来导入。</span></p>
<p>&nbsp;</p>
<p><span>在资源文件中，对话框资源只记录定义值，如对话框的大小、位置等，并非真正存储对话框最后显示在屏幕上的像素。这些大小、位置等信息最后由</span><span>Windows</span><span>解释后才在屏幕上被绘画成像素；菜单、字符串、快捷键等由文本构成；图形资源则真正由像素组成，它们在资源脚本中被定义为一个文件名，由资源编译器从磁盘文件导入。</span><span>Windows</span><span>在资源文件中支持的图形文件有</span><span>bmp</span><span>位图文件、</span><span>cur</span><span>光标文件和</span><span>ico</span><span>图标文件。</span><span>wav</span><span>声音文件也是得到支持的。</span></p>
<p>&nbsp;</p>
<p><span>编译好目标文件</span><span>*.obj</span><span>和资源文件</span><span>*.res</span><span>后，最后一步是用链接器将它们链接成可执行文件。链接的时候要用到函数库。在</span><span>DOS</span><span>环境下编程的时候，使用的函数库是静态库。静态库是一些已经编译好的代码模块。当用户在源程序中用到某个函数的时候，链接器从库文件中将这个函数的二进制代码取出，和</span><span>obj</span><span>文件合在一起生成最终的</span><span>exe</span><span>文件。但在</span><span>Win32</span><span>环境下，大部分的公用函数封装在</span><span>DLL</span><span>文件中，以动态链接的方式供用户程序调用。这时候库文件中只需要包含函数在</span><span>DLL</span><span>中的位置信息，不再需要有二进制代码部分。所以链接的时候也只是把库文件中的位置信息取出放入最后的可执行文件中。</span><span>Win32</span><span>中这种只包含位置信息的库文件称为导入库。</span></p>
<p>&nbsp;</p>
<p><span>Win32</span><span>汇编编程中使用不同汇编编译器的时候，汇编源程序的格式和资源脚本文件的格式可能稍微有所不同。各种头文件、库文件的文件名也有所不同。所以在开始编程之前，必须先选定一种合适的编译器。</span></p>
<p>&nbsp;</p>
<p><span>编译器和链接器</span></p>
<p><span>选择</span><span>MASM32</span><span>软件包，它是不同工具软件的大集合，它的汇编编译器用的是</span><span>MASM</span><span>软件包中的</span><span>ML.exe</span><span>，资源编译器和</span><span>32</span><span>位链接器用的是</span><span>Microsoft Visual Studio</span><span>中的</span><span>Rc.exe</span><span>和</span><span>Link.exe</span><span>，同时包含了</span><span>Microsoft Visual Studio</span><span>中的其他一些工具，如</span><span>Lib.exe</span><span>和</span><span>DumpPe.exe</span><span>等，所有的工具都适合于</span><span>Win32</span><span>编程的版本。</span></p>
<p>&nbsp;</p>
<p><span>MASM32</span><span>软件包包括了详尽的头文件和导入库文件，导入库文件取自</span><span>Visual C++</span><span>的导入库，规模庞大的头文件则是发布者整理的，软件包中还包括了很多例子，涉及</span><span>Win32</span><span>汇编的很多方面，例子收集自世界各地</span><span>Win32</span><span>汇编爱好者发布的源程序。</span></p>
<p>&nbsp;</p>
<p><span>MASM32</span><span>软件包使汇编不再只用来编写简单的程序和少量的核心模块，它的目标完全是为了用汇编写出专业的大型程序。虽然它是一个大杂烩，但发布者做了所有汇编程序中都想做、却又在庞大的工作量前止步的工作：收集合适的工具软件，收集导入库，整理出完整的头文件，收集例子文件，写帮助文档。</span></p>
<p><span>感谢发布者</span><span>Steve Hutchesson</span><span>为所有的汇编程序员所做的这一切。</span></p>
<p><span>最新版本的</span><span>MASM32</span><span>软件包可以在发布者的主页</span><span><a href="http://www.movsd.com/">http://www.movsd.com/</a></span><span>中下载。</span></p>
<p><span>MASM32</span><span>是一个免费的软件包，但其中的不同部分如编译器和例子程序等可能属于不同的公司和个人，使用时需要遵从他们的版权声明。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>创建资源</span></p>
<p><span>资源编译器的使用</span></p>
<p><span>可以使用</span><span>Visual C++</span><span>的资源编辑器。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>make</span><span>工具</span></p>
<p><span>在</span><span>DOS</span><span>时期编写汇编程序的时候，编译器和链接器基本上不用什么参数，命令只有区区两条：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Mask xxx.asm;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Link xxx.obj;</span></p>
<p><span>只要做个批处理把</span><span>xxx</span><span>换成</span><span>%1</span><span>，然后在命令行键入</span><span>asm.bat xxx</span><span>就万事大吉了，很是方便。</span></p>
<p>&nbsp;</p>
<p><span>Win32</span><span>编程就不一样了，不管编译器还是链接器都需要加上必要的选项，文件列表也多了起来，如链接器的命令行参数中要列出</span><span>obj</span><span>，</span><span>lib</span><span>，</span><span>res</span><span>和</span><span>def</span><span>等多种文件，又多了资源编译这一步，如果用批处理实现，要加的参数太多太乱，而每次用手工一行行地键入命令的话，那对程序员来说简直就是一场灾难。当然，一种简单的解决办法就是为每个编程项目单独建立一个批处理，每次改动后，运行批处理把所有模块重新编译一次，但是当程序很庞大的时候，这将花费很长时间，那么该如何处理呢？这时候就要用到</span><span>make</span><span>工具来维护代码了。</span></p>
<p>&nbsp;</p>
<p><span>make</span><span>工具可以看成是一个智能的批处理工具，它本身并没有编译和链接的功能，同样是用类似于批处理的方式，通过调用用户指定的语句来进行编译和链接。但是，批处理会执行全部命令将全部资源文件编译，包括那些不必重新编译的源文件，而</span><span>make</span><span>工具则可以根据目标文件上一次编译的时间和所依赖的源文件的更新时间自动判断应当编译哪些源文件，对没有更新过的文件不会处理，这样就可以大大提高程序调试的效率。</span></p>
<p>&nbsp;</p>
<p><span>Microsoft</span><span>的</span><span>make</span><span>工具文件名为</span><span>nmake.exe</span><span>，它并不是</span><span>MASM</span><span>软件包的一部分，但可以在</span><span>Visual C++</span><span>的</span><span>Bin</span><span>目录下找到。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>nmake</span><span>的用法</span></p>
<p><span>在命令行键入</span><span>nmake/?</span><span>可以显示帮助信息，</span><span>nmake</span><span>的语法为：</span></p>
<p><span>nmake [</span><span>选项</span><span>] [/f </span><span>描述文件名</span><span>] [x/ </span><span>输出信息文件名</span><span>] [</span><span>宏定义</span><span>] [</span><span>目标</span><span>]</span></p>
<p>&nbsp;</p>
<p><span>说明如下：</span></p>
<p><span>/f </span><span>参数</span> <span>——如果描述文件名不使用默认的</span><span>makefile</span><span>，可以用</span><span>/f</span><span>参数指定。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>描述文件的语法</span></p>
<p><span>make</span><span>工具最主要也是最基本的功能就是通过描述文件来描述源程序之间的相互关系并自动维护编译工作，而描述文件需要按照某种语法进行编写，文件中需要说明如何编译各个源文件并链接生成可执行文件，并要求定义源文件之间的依赖关系，为了更方便使用，文件中同时可以用一些宏定义。描述文件一般需要包含以下内容：</span></p>
<p><span>1</span><span>）注释</span></p>
<p><span>2</span><span>）宏定义</span></p>
<p><span>3</span><span>）显式规则</span></p>
<p><span>4</span><span>）隐含规则</span></p>
<p>&nbsp;</p>
<p><span># nmake</span><span>工具的描述文件例子</span></p>
<p><span>EXE = Test.exe<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>#</span><span>指定输出文件</span></p>
<p><span>OBJS = x.obj\</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>y.obj<span>&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; </span>#</span><span>需要的目标文件，可用反斜杠换行</span></p>
<p><span>RES = x.res<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>#</span><span>需要的资源文件</span></p>
<p>&nbsp;</p>
<p><span>LINK_FLAG = /subsystem:windows<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>#</span><span>链接选项</span></p>
<p><span>ML_FLAG = /c /coff<span>&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; </span>#</span><span>编译选项</span></p>
<p>&nbsp;</p>
<p><span>#</span><span>定义依赖关系和执行命令</span></p>
<p><span>$(EXE): $(OBJS)&nbsp;$(RES)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Link $(LINK_FLAG) /out:$(EXE) $(OBJS) $(RES)</span></p>
<p><span>$(OBJS):Common.inc</span></p>
<p><span>y.obj:y.inc</span></p>
<p>&nbsp;</p>
<p><span>#</span><span>定义汇编编译和资源编译的默认规则</span></p>
<p><span>.asm.obj:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ml $(ML_FLAG) $&lt;</span></p>
<p><span>.rc .res:</span></p>
<p><span>rc $&lt;</span></p>
<p>&nbsp;</p>
<p><span>#</span><span>清除临时文件</span></p>
<p><span>clean:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><st1:state w:st="on"><st1:place w:st="on">del</st1:place></st1:state> *.obj</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><st1:state w:st="on"><st1:place w:st="on">del</st1:place></st1:state> *.res</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>构建开发环境</span></p>
<p><span>以下是推荐的一种开发环境。</span></p>
<p><span>1</span><span>）安装常用软件，包括文本编辑软件</span><span>EditPlus</span><span>，</span><span>MSDN</span><span>，</span><span>16</span><span>进制编辑器</span><span>hexedit</span><span>，调用工具</span><span>Soft-ICE</span><span>和反汇编软件</span><span>W32DASM</span><span>等。资源编辑器用</span><span>Visual C++</span><span>集成的。</span></p>
<p><span>2</span><span>）选择一个驱动器安装</span><span>MASM32</span><span>软件包，安装好的目录是</span><span>x:\masm32 </span><span>目录，整个软件包中重要的只有</span><span>3</span><span>个目录：</span><span>bin</span><span>目录中有汇编编译器</span><span>ml.exe</span><span>，资源编译器</span><span>rc.exe</span><span>和链接器</span><span>Link.exe</span><span>等执行文件；</span><span>include</span><span>目录中有各种头文件；</span><span>lib</span><span>目录中有全部导入库。</span></p>
<p><span>3</span><span>）建立源文件目录，由于</span><span>Win32</span><span>汇编不再像</span><span>DOS</span><span>汇编一样一个项目只有一个</span><span>asm</span><span>文件，而包括</span><span>asm,rc,makefile</span><span>和图标等多个文件，如果把多个项目的文件混在同一个目录中将无法分辨，所以必须为每个项目单独建立一个目录，建议把这些目录集中在一个专门放置程序的目录中，如</span><span>x:\source</span><span>目录。</span></p>
<p><span>4</span><span>）由于</span><span>MASM32</span><span>软件包中没有</span><span>nmake.exe</span><span>文件，所以要单独寻找</span><span>namke.exe</span><span>并拷贝到</span><span>bin</span><span>目录中。</span></p>
<p><span>5</span><span>）为这个环境建立一个设置环境变量的批处理文件，假设文件名为</span><span>var.bat</span><span>，那么这个文件内容如下：</span></p>
<p><span>@echo off</span></p>
<p><span>set include=x:\masm32\include</span></p>
<p><span>set lib=x:\masm32\lib</span></p>
<p><span>set path=x:\masm32\bin;%path%</span></p>
<p><span>echo on</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>尝试编译第一个程序</span></p>
<p><span>ms-dos -&gt; var.bat -&gt; nmake</span><span>；即可生成一个</span><span>exe</span><span>文件。</span></p>
<p><span>下面的工作就是：编辑源程序</span><span>-&gt;</span><span>切换到</span><span>ms-dos</span><span>窗口</span><span>-&gt;</span><span>键入</span><span>nmake</span><span>编译</span><span>-&gt;</span><span>运行生成的可执行文件</span><span>-&gt;</span><span>切换到文本编辑器修改源程序&#8230;&#8230;如此循环往复调试程序。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-06 16:25 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/06/122457.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Win32汇编--需要了解的基础知识</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/06/122435.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Fri, 06 Aug 2010 05:42:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/06/122435.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122435.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/06/122435.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122435.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122435.html</trackback:ping><description><![CDATA[<p>在一个月前我打算学Win32汇编，发现汇编的基础不是太好，便花了近一个月的时间把王爽老师的《汇编语言》看了一遍。现在再来看Win32汇编，比一个月前轻松了不少。真是本看书，对于我个人来说。<br><br>接下来，我将继续Win32汇编的学习。坚持、坚持！<br><br>Let's go!<br><br>&nbsp; </p>
<p><span>Background</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>环境下</span><span>32</span><span>位汇编语言是一种全新的编程语言。它使用与</span><span>C++</span><span>语言相同的</span><span>API</span><span>接口，不仅可以用来开发出大型的软件，而且是了解操作系统运行细节的最佳方式。</span></p>
<p>&nbsp;</p>
<p><span>Win32</span><span>指的是</span><span>32</span><span>位的</span><span>Windows</span><span>系列操作系统。</span></p>
<p>&nbsp;</p>
<p><span>1990</span><span>年</span><span>5</span><span>月份微软推出了</span><span>Windows 3.0</span><span>，可以支持</span><span>Intel 80286/386/486</span><span>微处理器的保护模式，并可以访问达</span><span>16MB</span><span>的内存。</span><span>Windows 3.0</span><span>一面世便在商业上取得了惊人的成功，从而一举奠定了</span><span>Microsoft</span><span>在操作系统上的垄断地位。</span></p>
<p>&nbsp;</p>
<p><span>1992</span><span>年</span><span>4</span><span>月，</span><span>Microsoft</span><span>推出了更稳定的</span><span>Windows 3.1</span><span>，可以支持</span><span>True Type</span><span>字体。</span><span>Windows 3.1</span><span>是</span><span>16</span><span>位</span><span>Windows</span><span>中最流行的版本。</span></p>
<p>&nbsp;</p>
<p><span>1993</span><span>年</span><span>5</span><span>月，</span><span>Microsoft</span><span>发布了具备安全性和稳定性特征的</span><span>32</span><span>位操作系统</span><span>Windows NT 3.11</span><span>，主要针对网络和服务器市场。</span><span>NT</span><span>代表新技术（</span><span>New Technology</span><span>）。</span><span>NT 3.11</span><span>是</span><span>Windows</span><span>系列中使用</span><span>32</span><span>位编程模式的第一个版本。它充分利用</span><span>80386</span><span>及以上处理器的平面地址空间和保护模式等新技术。</span></p>
<p>&nbsp;</p>
<p><span>1995</span><span>年</span><span>8</span><span>月，</span><span>Microsoft</span><span>推出新一代操作系统</span><span>Windows 95</span><span>。</span><span>Windows 95</span><span>实现了很友好的用户界面，支持即插即用功能，支持主流多媒体设备和</span><span>DirectX</span><span>编程接口，成为</span><span>Microsoft</span><span>发展史上的一个里程碑，也是操作系统发展史上的一个里程碑。从此，</span><span>Windows 9x</span><span>便取代了</span><span>Windows 3.x</span><span>和</span><span>MS-DOS</span><span>操作系统，成为个人计算机平台的主流操作系统。</span></p>
<p>&nbsp;</p>
<p><span>1998</span><span>年</span><span>Microsoft</span><span>又发布了使用更方便的</span><span>Windows 98</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>在操作系统的分类上，</span><span>Microsoft</span><span>根据家庭个人用户和商业办公用户的不同需求，分别提供</span><span>Windows 9x</span><span>和</span><span>Windows NT</span><span>系列，</span><span>Windows 9x</span><span>注重用户界面及其他易用性特征，而</span><span>NT</span><span>系列则在纯</span><span>32</span><span>位内核的稳定性和可靠性等企业级特征上下功夫。</span></p>
<p>&nbsp;</p>
<p><span>2000</span><span>年，微软发布采用纯</span><span>32</span><span>位内核并照顾了家庭消费类应用软件的</span><span>Windows NT 5.0</span><span>，即</span><span>Windows 2000</span><span>。至此</span><span>Micosoft</span><span>的两个系列操作系统（</span><span>9x</span><span>和</span><span>NT</span><span>）终于开始统一。</span></p>
<p>&nbsp;</p>
<p><span>为了利用</span><span>MS-DOS</span><span>时代大量的应用程序，保持向下的兼容性，</span><span>Windows 9x</span><span>的内核模块还有许多地址使用</span><span>16</span><span>位程序，但在编程上，支持</span><span>32</span><span>位的编程模式。</span></p>
<p>&nbsp;</p>
<p><span>Windows NT</span><span>系列和</span><span>Windows 9x</span><span>系列操作系统都支持</span><span>Win32 API</span><span>（</span><span>Application Programming Interface</span><span>），即</span><span>Windows 32</span><span>位应用程序编程接口，</span><span>Win32 API</span><span>为应用程序提供了大量的系统功能调用，通过</span><span>Win32 API</span><span>调用</span><span>Windows</span><span>系统相当于在</span><span>MS-DOS</span><span>中通过中断方式调用系统功能。就像</span><span>DOS</span><span>汇编程序中随处可见的</span><span> INT 21h</span><span>指令一样，</span><span>Windows</span><span>应用程序中</span><span>Win32 API</span><span>也随处可见。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>的特色</span></p>
<p><span>对于使用者来说：</span></p>
<p><span>1</span><span>）图形用户界面；（</span><span>GUI</span><span>，</span><span>Graphic User Interface</span><span>）</span><span> Windows</span><span>最重要的特色。</span></p>
<p><span>2</span><span>）一致的用户界面；</span><span>&nbsp;</span><span>便于使用。</span></p>
<p><span>3</span><span>）多任务。</span> <span>非常重要的特色。用户可以同时运行多个程序，可以在不同的程序之间传送数据。</span></p>
<p>&nbsp;</p>
<p><span>对于程序员来说，更关心隐藏在底下的东西：</span></p>
<p><span>1</span><span>）<strong>大量的函数调用</strong>；</span><span>Win32</span><span>支持上千种函数的调用，几乎涉及所有的方面，程序员可以把更多的时间放在程序的逻辑结构和用户界面上。</span></p>
<p><span>2</span><span>）<strong>和设备的无关性</strong>；</span><span>Win32</span><span>程序并不直接访问屏幕、打印机和键盘等硬件设备，</span><span>Windows</span><span>虚拟了所有的硬件。只要有硬件的设备驱动程序，这个硬件就可以使用，应用程序并不需要关心硬件的具体型号。与</span><span>DOS</span><span>编程中需要针对不同的显示卡和打印机等编写很多的驱动程序来比，这个特性对程序员的帮助是巨大的。</span></p>
<p><span>3</span><span>）<strong>内存管理</strong>。由于内存分页和虚拟内存的使用，每个程序都可以使用</span><span>4GB</span><span>的地址空间，</span><span>DOS</span><span>编程时必须考虑的</span><span>640KB</span><span>的内存问题已经成为历史。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>必须了解的东西</span></p>
<p>&nbsp;</p>
<p><span>80x86</span><span>处理器的工作模式</span></p>
<p>&nbsp;</p>
<p><span>80386</span><span>处理器有</span><span>3</span><span>种工作模式：实模式、保护模式和虚拟</span><span>86</span><span>模式。</span></p>
<p><span>实模式和虚拟</span><span>86</span><span>模式是为了和</span><span>8086</span><span>处理器兼容而设置的。在实模式下，</span><span>80386</span><span>处理器就相当于一个快速的</span><span>8086</span><span>处理器。</span></p>
<p><span>保护模式是</span><span>80386</span><span>处理器的主要工作模式。在此方式下，</span><span>80386</span><span>可以寻址</span><span>4GB</span><span>的地址空间，同时，保护模式提供了</span><span>80386</span><span>先进的多任务、内存分页管理和优先级保护等机制。</span></p>
<p>&nbsp;</p>
<p><span>为了在保护模式下继续提供和</span><span>8086</span><span>处理器的兼容，</span><span>80386</span><span>又设计了一种虚拟</span><span>86</span><span>模式，以便可以在保护模式的多任务条件下，有的任务运行</span><span>32</span><span>位程序，有的任务运行</span><span>MS-DOS</span><span>程序。</span></p>
<p><span>在虚拟</span><span>86</span><span>模式下，同样支持任务切换、内存分页管理和优先级，但内存的寻地址方式和</span><span>8086</span><span>相同，也是可以寻址</span><span>1MB</span><span>的空间。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>实模式是</span><span>80386</span><span>处理器工作的基础，这时</span><span>80386</span><span>当做一个快速的</span><span>8086</span><span>处理器工作。在实模式下可以通过指令切换到保护模式，也可以从保护模式退回实模式。</span></p>
<p><span>虚拟</span><span>86</span><span>模式则以保护模式为基础，在保护模式和虚拟</span><span>86</span><span>模式之间可以互相切换，但不能从实模式直接进入虚拟</span><span>86</span><span>模式或从虚拟</span><span>86</span><span>模式直接退到实模式。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>实模式</span></p>
<p><span>80386</span><span>处理器被复位或加电的时候以实模式启动。这时候处理器中的各寄存器以实模式的初始化值工作。</span><span>80386</span><span>处理器在实模式下的存储器寻址方式和</span><span>8086</span><span>是一样的，由段寄存器的内容乘以</span><span>16</span><span>当做基地址，加上段内的偏移地址形成最终的物理地址，这时候它的</span><span>32</span><span>位地址线只使用了低</span><span>20</span><span>位。在实模式下，</span><span>80386</span><span>处理器不能对内存进行分页管理，所以指令寻址的地址就是内存中实际的物理地址。在实模式下，所有的段都是可以读、写和执行的。</span></p>
<p>&nbsp;</p>
<p><span>实模式下</span><span>80386</span><span>不支持优先级，所有的指令相当于在工作在特权级（优先级</span><span>0</span><span>），所以它可以执行所有特权指令，包括读写控制寄存器</span><span>CR0</span><span>等。实际上，</span><span>80386</span><span>就是通过在实模式下初始化控制寄存器，</span><span>GDTR</span><span>，</span><span>LDTR</span><span>，</span><span>IDTR</span><span>与</span><span>TR</span><span>等管理寄存器以及页表，然后再通过加载</span><span>CR0</span><span>使其中的保护模式使能位置位而进入保护模式的。实模式下不支持硬件上的多任务切换。</span></p>
<p>&nbsp;</p>
<p><span>实模式下的中断处理方式和</span><span>8086</span><span>处理器相同，也用中断向量表来定位中断服务程序地址。中断向量表的结构也和</span><span>8086</span><span>处理器一样，每</span><span>4</span><span>个字节组成一个中断向量，其中包括两个字节的段地址和两个字节的偏移地址。</span></p>
<p>&nbsp;</p>
<p><span>从编程的角度看，除了可以访问</span><span>80386</span><span>新增的一些寄存器外，实模式的</span><span>80386</span><span>处理器相比</span><span>8086</span><span>，其最大的好处是可以使用</span><span>80386</span><span>的</span><span>32</span><span>位寄存器，用</span><span>32</span><span>位寄存器进行编程可以使计算程序更加简捷，加快了执行速度。其次，</span><span>80386</span><span>中增加的两个辅助段寄存器</span><span>FS</span><span>和</span><span>GS</span><span>在实模式下也可以使用，这样，同时可以访问的段达到了</span><span>6</span><span>个而不必考虑重新装入的问题；最后，很多</span><span>80386</span><span>的新增指令也使一些原来不很方便的操作得以简化，如</span><span>80386</span><span>中可以使用下述指令进行数组访问：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,[eax + ebx * 2 + </span><span>数组基地址</span><span>]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>这相当于把数组下标为</span><span>eax</span><span>和</span><span>ebx</span><span>的项目放入</span><span>cx</span><span>中；</span><span>ebx*2</span><span>中的</span><span>2</span><span>可以是</span><span>1,2,4</span><span>或</span><span>8</span><span>，这样就可以支持</span><span>8</span><span>位到</span><span>64</span><span>位的数组。</span></p>
<p><span>另外，</span><span>pushad</span><span>和</span><span>popad</span><span>指令可以一次把所有</span><span>8</span><span>个通用寄存器的值压入或从堆栈中弹出，比起用下面的指令分别将</span><span>8</span><span>个寄存器入栈要快了很多：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push eax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push ebx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ebx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop eax</span></p>
<p>&nbsp;</p>
<p><span>当然，使用了这些新指令的程序是无法拿回到</span><span>8086</span><span>处理器上去执行的，因为这些指令的编码在</span><span>8086</span><span>处理器上是未定义的。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>保护模式</span></p>
<p><span>当</span><span>80386</span><span>工作在保护模式下的时候，它的所有功能都是可用的。这时</span><span>80386</span><span>所有的</span><span>32</span><span>根地址线都可供寻址，物理寻地址空间高达</span><span>4GB</span><span>。在保护模式下，支持内存分页机制，提供了对虚拟内存的良好支持。虽然与</span><span>8086</span><span>可寻址的</span><span>1MB</span><span>物理地址空间相比，</span><span>80386</span><span>可寻地址的物理地址空间可谓很大，但实际的微机系统不可能安装如此大的物理内存。所以，为了运行大型程序和真正实现多任务，虚拟内存是一种必需的技术。</span></p>
<p>&nbsp;</p>
<p><span>保护模式下</span><span>80386</span><span>支持多任务，可以依靠硬件仅在一条指令中实现任务切换。任务环境的保护工作是由处理器自动完成的。在保护模式下，</span><span>80386</span><span>处理器还支持优先级机制，不同的程序可以运行在不同的优先级上。优先级一共分</span><span>0~3 </span><span>四个级别，操作系统运行在最高的优先级</span><span>0</span><span>上，应用程序则运行在比较低的级别上；配合良好的检查机制后，既可以在任务间实现数据的安全共享也可以很好地隔离各个任务。从实模式切换到保护模式是通过修改控制寄存器</span><span>CR0</span><span>的控制位</span><span>PE</span><span>（位</span><span>0</span><span>）来实现的。在这之前还需要建立保护模式必需的一些数据表，如全局描述符表</span><span>GDT</span><span>和中断描述符表</span><span>IDT</span><span>等。</span></p>
<p>&nbsp;</p>
<p><span>DOS</span><span>操作系统运行于实模式下，而</span><span>Windows</span><span>操作系统运行于保护模式下。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>虚拟</span><span>86</span><span>模式</span></p>
<p><span>虚拟</span><span>86</span><span>模式是为了在保护模式下执行</span><span>8086</span><span>程序而设置的。虽然</span><span>80386</span><span>处理器已经提供了实模式来兼容</span><span>8086</span><span>程序，但这时</span><span>8086</span><span>程序实际上只是运行得快了一点，对</span><span>CPU</span><span>的资源还是独占的。在保护模式的多任务环境下运行这些程序时，它们中的很多指令和保护模式环境格格不入，如段寻址方式、对中断的处理和</span><span>I/O</span><span>操作的特权问题等。为了在保护模式下工作而丢弃这些程序的代价是巨大的。设想一下，如果</span><span>Windows</span><span>或</span><span>80386</span><span>处理器推出的时候宣布不能运行以前的</span><span>MS-DOS</span><span>程序，那么就等于放弃了一个巨大的软件库，</span><span>Windows</span><span>以及</span><span>80386</span><span>处理器可能就会落得和苹果机一样的下场，这是</span><span>Microsoft</span><span>和</span><span>Intel</span><span>都不愿意看到的。所以，</span><span>80386</span><span>处理器又设计了一个虚拟</span><span>86</span><span>模式。</span></p>
<p>&nbsp;</p>
<p><span>虚拟</span><span>86</span><span>模式是以任务形式在保护模式上执行的，在</span><span>80386</span><span>上可以同时支持由多个真正的</span><span>80386</span><span>任务和虚拟</span><span>86</span><span>模式构成的任务。在虚拟</span><span>86</span><span>模式下，</span><span>80386</span><span>支持任务切换和内存分页。在</span><span>Windows</span><span>操作系统中，有一部分程序专门用来管理虚拟</span><span>86</span><span>模式的任务，称为虚拟</span><span>86</span><span>管理程序。</span></p>
<p>&nbsp;</p>
<p><span>既然虚拟</span><span>86</span><span>模式以保护模式为基础，它的工作方式实际上是实模式和保护模式的混合。为了和</span><span>8086</span><span>程序的寻地址方式兼容，虚拟</span><span>86</span><span>模式采用和</span><span>8086</span><span>一样的寻址方式，即用段寄存器乘以</span><span>16</span><span>当作基址再配合偏移地址形成线性地址，寻址空间为</span><span>1MB</span><span>。但显然多个虚拟</span><span>86</span><span>任务不能同时使用同一位置的</span><span>1MB</span><span>地址空间，否则会引起冲突。操作系统利用分页机制将不同虚拟</span><span>86</span><span>任务的地址空间映射到不同的物理地址上去，这样每个虚拟</span><span>86</span><span>任务看起来都认为自己在使用</span><span>0~1MB</span><span>的地址空间。</span></p>
<p>&nbsp;</p>
<p><span>8086</span><span>代码中有相当一部分指令在保护模式下属于特权指令，如屏幕中断的</span><span>cli</span><span>和中断返回指令</span><span>iret</span><span>等。这些指令在</span><span>8086</span><span>程序中是合法的。如果不让这些指令执行，</span><span>8086</span><span>代码就无法工作。为了解决这个问题，虚拟</span><span>86</span><span>管理程序采用模拟的方法完成这些指令。这些特权指令执行的时候引起了保护异常。虚拟</span><span>86</span><span>管理程序在异常处理程序中检查产生异常的指令，如果是中断指令，则从虚拟</span><span>86</span><span>任务的中断向量表中取出中断处理程序的入口地址，并将控制转移过去；如果是危及操作系统的指令，如</span><span>cli</span><span>等，则简单地忽略这些指令，在异常处理程序返回的时候直接返回到下一条指令。通过这些措施，</span><span>8086</span><span>程序既可以正常地运行下去，在执行这些指令的时候又觉察不到已经被虚拟</span><span>86</span><span>管理程序做了手脚。</span><span>MS-DOS</span><span>应用程序在</span><span>Windows</span><span>操作系统中就是这样工作的。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>Windows</span><span>的内存管理</span></p>
<p><span>Win32</span><span>汇编中，每个程序都可以用</span><span>4GB</span><span>的内存吗？</span></p>
<p><span>Win32</span><span>汇编源代码中为什么看不到</span><span>CS</span><span>、</span><span>DS</span><span>、</span><span>ES</span><span>和</span><span>SS</span><span>等段寄存器的使用？</span></p>
<p>&nbsp;</p>
<p><span>DOS</span><span>操作系统的内存安排</span></p>
<p><span>Win32</span><span>编程相对于</span><span>DOS</span><span>编程最大的区别之一就是内存的使用。</span></p>
<p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=60>
            <p><span>00000h</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>中断向量表</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p><span>00400h</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>BIOS</span><span>数据区</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p><span>00500h</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>DOS</span><span>数据区</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>系统程序</span></p>
            <p align=center><span>（</span><span>DOS</span><span>的驻留部分、驱动程序等）</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>可用空间</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p><span>A0000h</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>图形模式视频缓冲区</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p><span>B0000h</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>单色字符模式视频缓冲区</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p><span>B8000h</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>彩色字符模式视频缓冲区</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p><span>C0000h</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>VGA BIOS</span><span>地址</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p><span>C8000h</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>ROM</span><span>扩展、系统</span><span>BIOS</span><span>地址</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p><span>FFFFFh</span></p>
            </td>
            <td vAlign=top width=233>
            <p align=center><span>64KB</span><span>高端内存</span></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>在实模式下，一个内存寻址方式是，一个完整的地址由段地址和偏移地址两部分组成。段地址放在</span><span>16</span><span>位的段寄存器中，然后在指令中用</span><span>16</span><span>位的偏移地址寻址。处理器换算时先将段地址乘以</span><span>10h</span><span>（即</span><span>16</span><span>），得到段在物理内存中的起始地址；然后</span> <span>加上</span><span>16</span><span>位的偏移地址得到实际的物理地址。</span></p>
<p>&nbsp;</p>
<p><span>当</span><span>80386</span><span>处理器工作在保护模式和虚拟</span><span>8086</span><span>模式的时候，可以使用全部</span><span>32</span><span>根地址线访问内存</span><span>4GB</span><span>大的内存。段地址加偏移地址的计算方法显然无法覆盖这么大的范围。但计算一下就可以发现，实际上和</span><span>8086</span><span>同样的限制已经不复存在，因为</span><span>80386</span><span>所有的通用寄存器都是</span><span>32</span><span>位的，</span><span>2</span><span>的</span><span>32</span><span>次方相当于</span><st1:chmetcnv w:st="on" UnitName="g" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0"><span>4G</span></st1:chmetcnv><span>，所以用任何一个通用寄存器来间接寻址，不必分段就已经可以访问到所有的内存地址。</span></p>
<p>&nbsp;</p>
<p><span>这是不是说，在保护模式下，段寄存器就不再有用了呢？答案是否定的。实际上段寄存器更有用了，虽然在寻址上不再有分段的限制问题，但在保护模式下，一个地址空间是否可以被写入，可以被多少优先级的代码写入，是不是允许执行等涉及保护的问题就出来了。要解决这些问题，必须对一个地址空间定义一些安全上的属性。段寄存器这时就派上了用途。但是涉及属性和保护模式下段的其他参数，要表示的信息太多了，要用</span><span>64</span><span>位长的数据才能表示。我们把这</span><span>64</span><span>位的属性数据叫做段描述符（</span><span>Segment Desciptor</span><span>）。</span></p>
<p>&nbsp;</p>
<p><span>80386</span><span>的段寄存是</span><span>16</span><span>位的，无法放下保护模式下</span><span>64</span><span>位的段描述符。解决办法是把所有段的段描述符顺序放在内存中的指定位置，组成一个段描述符表（</span><span>Descriptor Table</span><span>）；而段寄存器中的</span><span>16</span><span>位用来做索引信息，指定这个段的属性用段描述符表中的第几个描述符来表示。这时，段寄存器中的信息不再是段地址了，而是段选择器（</span><span>Segment Selector</span><span>）。可以通过它在段描述符表中，选择一个项目以得到段的全部信息。</span></p>
<p>&nbsp;</p>
<p><span>既然这样，段描述符表放在哪里呢？</span><span>80386</span><span>中引入了两个新的寄存器来管理段描述符表。一个是</span><span>48</span><span>位的全局描述符表寄存器</span><span>GDTR</span><span>（</span><span>Global Descriptor Table Register</span><span>），一个是</span><span>16</span><span>位的局部描述符表寄存器</span><span>LDTR</span><span>（</span><span>Local Descriptor Table Register</span><span>）。那么，为什么有两个描述符表寄存器呢？</span></p>
<p>&nbsp;</p>
<p><span>GDTR</span><span>指向的描述符表为全局描述符表</span><span>GDT</span><span>（</span><span>Global Descriptor Table</span><span>）。它包含系统中所有任务都可用的段描述符，通常包含描述操作系统所使用的代码段、数据段和堆栈段的描述符及各任务的</span><span>LDT</span><span>段等；全局描述符表只有一个。</span></p>
<p>&nbsp;</p>
<p><span>LDTR</span><span>则指向局部描述符表</span><span>LDT</span><span>（</span><span>Local Descriptor Table</span><span>）。</span><span>80386</span><span>处理器设计成每个任务都有一个独立的</span><span>LDT</span><span>。它包含有每个任务私有的代码段、数据段和堆栈段的描述符，也包含该任务所使用的一些门描述符，如任务门和调用门描述符等。</span></p>
<p>&nbsp;</p>
<p><span>不同任务的局部描述符表分别组成不同的内存段，描述这些内存段的描述符当做系统描述符放在全局描述符表中。和</span><span>GDTR</span><span>直接指向内存地址不同，</span><span>LDTR</span><span>和</span><span>CS</span><span>，</span><span>DS</span><span>等段选择器一样只存放索引值，指向局部描述符表内存段对应的描述符在全局描述符表中的位置。随着任务的切换，只要改变</span><span>LDTR</span><span>的值，系统当前的局部描述符表</span><span>LDT</span><span>也随之切换，这样便于各任务之间数据的隔离。但</span><span>GDT</span><span>并不随着任务的切换而切换。</span></p>
<p>&nbsp;</p>
<p><span>看到这里，读者可能会提出一个问题，既然有全局描述符表和局部描述符表两个表，那么段选择器中的索引值对应哪个表中的描述符呢？实际上，</span><span>16</span><span>位的段选择器中只有高</span><span>13</span><span>位表示索引值。剩下的</span><span>3</span><span>个数据位中，第</span><span>0</span><span>，</span><span>1</span><span>位表示程序的当前优先</span><span>RPL</span><span>；第</span><span>2</span><span>位</span><span>TI</span><span>位用来表示在段描述符的位置；</span><span>TI=0</span><span>表示在</span><span>GDT</span><span>中，</span><span>TI=1</span><span>表示在</span><span>LDT</span><span>中。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>80386</span><span>的内存分页机制</span></p>
<p><span>在实模式下寻址的时候，段寄存器</span><span>+</span><span>偏移地址经过转换计算以后得到的地址是物理地址，也就是在物理内存中的实际地址。</span></p>
<p><span>而保护模式下，段选择器</span><span>+</span><span>偏移地址转换后的地址被称为线性地址，而不是物理地址。</span></p>
<p>&nbsp;</p>
<p><span>线性地址是物理地址吗？</span></p>
<p><span>可能是，也可能不是，这取决于</span><span>80386</span><span>的内存分页机制是否被使用。</span></p>
<p><span>在单任务的</span><span>DOS</span><span>系统中，一个应用程序可以使用所有的空闲内存。程序退出后，操作系统回收所有的碎片内存并且合并成一个大块内存继续供下一个程序使用。内存合并过程中的一个极端情况汇报系统中有多个</span><span>TSR</span><span>卸载后，后装入的</span><span>TSR</span><span>会留存内存的中间部位，把空闲内存隔成两个区域。这时应用程序使用的最大内存块只能是这两块内存中较大的一块，无法将它们合并使用。</span></p>
<p>&nbsp;</p>
<p><span>对于一个多任务的操作系统，内存的碎片化是不能容忍的。否则，经过一段时间后，即使空闲内存的总和很大，也可能出现任何一处内存都小到无法装入执行程序的地步。所以多任务操作系统中碎片内存的合并是个很重要的问题。</span></p>
<p>&nbsp;</p>
<p><span>80386</span><span>处理器的分页机制可以很好地解决这个问题。</span><span>80386</span><span>处理器把</span><span>4KB</span><span>大小的一块内存当做一页内存，每页物理内存可以根据页目录和页表，随意映射到不同的线性地址上。这样，就可以将物理地址不连续的内存的映射连到一起，在线性地址上视为连续。在</span><span>80386</span><span>处理器中，除了和</span><span>CR3</span><span>寄存器（指定当前页目录的地址）相关的指令使用的是物理地址外，其他所有指令都是用线性地址寻址的。</span></p>
<p>&nbsp;</p>
<p><span>是否启用内存分页机制是由</span><span>80386</span><span>处理器新增的</span><span>CR0</span><span>寄存器中的位</span><span>31</span><span>（</span><span>PG</span><span>位）决定的。如果</span><span>PG=0</span><span>，则分页机制不启用，这时所有指令寻址的地址（线性地址）就是系统中实际的物理地址；当</span><span>PG=1</span><span>的时候，</span><span>80386</span><span>处理器进入内存分页管理模式，所有的线性地址要经过页表的映射才得到最后的物理地址。</span></p>
<p>&nbsp;</p>
<p><span>内存分页管理只能在保护模式下才可以实现，实模式不支持分页机制。但不管在哪种模式下，所有寻址指令使用的都是线性地址，程序不用关心数据最后究竟存放在物理内存的哪个地方。</span></p>
<p>&nbsp;</p>
<p><span>页表规定的不仅是地址的映射，同时还规定了页的访问属性，如是否可写、可读和可执行等。比如把代码所在的内存页设置为可读与可执行，那么权限不够的代码向它写数据就会引发保护异常。利用这个机制可以在硬件层次上支持虚拟内存的实现。</span></p>
<p>&nbsp;</p>
<p><span>页表可以指定一个页面并不真正映射到物理内存中，这样，访问这个页的指令会引发页异常错误。这时，处理器会自动转移到页异常处理程序中去，操作系统可以在异常处理程序中将硬盘上的虚拟内存读到内存中并修改页表重新映射，然后重新执行引发异常的指令。这样指令可以正常执行下去。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>Windows</span><span>的内存安排</span></p>
<p><span>Windows</span><span>系统一般在硬盘上建立大小为物理内存两倍左右的交换文件（文件名在</span><span>Windows 9x</span><span>下为</span><span>Win386.swp</span><span>，</span><span>Windows NT</span><span>下为</span><span>PageFile.sys</span><span>）用做虚拟内存。</span></p>
<p><span>利用</span><span>80386</span><span>处理器的内存分页机制，交换文件在寻地址上可以很方便地作为物理内存使用。只需在真正访问到的时候将硬盘文件的内容读入物理内存，然后重新将线性地址映射到这块物理内存就可以了。同样道理，被执行的可执行文件也不必真正装入内存，只要在页表中建立映射关系，以后到真正访问到的时候再调入物理内存。</span></p>
<p>&nbsp;</p>
<p><span>如果把虚拟内存暂时先视为物理内存的一部分，从物理内存中的层次看，</span><span>Windows</span><span>操作系统和</span><span>DOS</span><span>一样，也是所有的内容共享内存，如操作系统使用的代码和数据（</span><span>GDT</span><span>，</span><span>LDT</span><span>与页表等），当前执行中的所有程序的代码和数据以及这些程序调用的</span><span>DLL</span><span>的代码和数据等。</span></p>
<p>&nbsp;</p>
<p><span>但是从应用程序代码的层次看，也就是说从分页映射后线性地址层次看，内存的安排却不是这个样子。因为</span><span>Windows</span><span>是一个分时的多任务操作系统，</span><span>CPU</span><span>时间被分成一个个的时间片后分配给不同程序轮流使用，在一个程序的时间片中，和这个程序执行无关的东西（如其他程序的代码和数据）并不需要映射到线地址中去。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>操作系统通过切换不同的页表内容让线性地址在不同的时间片中映射不同的内容。在物理内存中，操作系统和系统</span><span>DLL</span><span>的代码需要供每个应用程序调用，所以在所有的时间片中都必须被映射；用户程序只在自己所属的时间片内被映射；而用户</span><span>DLL</span><span>则有选择地被映射。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>Win32</span><span>编程中几个很重要的概念：</span></p>
<p><span>每个应用程序都有自己的</span><span>4GB</span><span>的寻址空间。该空间可存放操作系统、系统</span><span>DLL</span><span>和用户</span><span>DLL</span><span>的代码，它们之中有各种函数供应用程序调用。再除去其他的一些空间，余下的是应用程序的代码、数据和可以分配的地址空间。</span></p>
<p>&nbsp;</p>
<p><span>不同应用程序的线性地址空间是隔离的。虽然它们在物理内存中同时存在，但在某个程序所属的时间片中，其他应用程序程序的代码和数据没有被映射到可寻地址的线性地址中，所以是不可访问的。从编程的角度看，程序可以使用</span><span>4GB</span><span>的寻址空间，而这个空间是私有的。</span></p>
<p>&nbsp;</p>
<p><span>DLL</span><span>程序没有自己的私有的空间。它们总是被映射到其他应用程序的地址空间中，当做和其他应用程序的一部分运行。原因很简单，如果它不和其他程序同属一个地址空间，应用程序该如何调用它呢？</span></p>
<p>&nbsp;</p>
<p><span>从</span><span>Win32</span><span>汇编的角度看内存寻址</span></p>
<p><span>DOS</span><span>下的分段寻地址方式令人一头雾水，</span><span>80386</span><span>保护模式的内存管理就更复杂。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>是一个多任务操作系统，最首要的宗旨就是稳定压倒一切，如果描述符表以及页表等内容交给用户程序是很不安全的，不用说全局描述符表，就是为每个程序建立的局部描述符表也不应该让用户程序改写，否则用户可以通过构造自己的描述符来访问操作系统不希望用户访问的东西。任何权限上开放引发的安全问题都是很严重的。</span></p>
<p><span>病毒程序就是利用这些系统漏洞，提升自身的权限，做一些破坏动作的。</span></p>
<p>&nbsp;</p>
<p><span>所以，</span><span>Windows</span><span>操作系统干脆为用户程序安排好了一切。具体表现在为用户程序的代码段、数据段和堆栈段全部预定义好了段描述符。这些段的起始地址为</span><span>0</span><span>，限长为</span><span>ffffffff</span><span>，所以用它们可以直接寻址全部的</span><span>4GB</span><span>地址空间。程序开始执行的时候，</span><span>CS</span><span>，</span><span>DS</span><span>，</span><span>ES</span><span>和</span><span>SS</span><span>都已经指向了正确的描述符，在整个程序的生命周期内，程序员不必改动这些段寄存器，也不必关心它们的值究竟是多少（实际上，想改也改不了）。</span></p>
<p>&nbsp;</p>
<p><span>所以对</span><span>Win32</span><span>汇编程序来说，整个源程序中竟然可以不用出现段寄存器的身影。这在</span><span>DOS</span><span>汇编编程中是不可想象的。</span></p>
<p>&nbsp;</p>
<p><span>并不是</span><span>Win32</span><span>汇编源代码用不到段寄存器，而是用户在使用中不必去关心段寄存器。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>Windows</span><span>的特权保护</span></p>
<p><span>Windows</span><span>的特权保护和处理器硬件的支持是分不开的。</span></p>
<p><span>优先级的划分、指令的权限检查和超出权限访问的异常处理等是构成特权保护的基础。</span></p>
<p><span>1</span><span>、</span><span>Win32</span><span>汇编中为什么找不到中断指令的使用？</span></p>
<p><span>2</span><span>、</span><span>Windows</span><span>错误的蓝屏屏幕是从哪里来的？</span></p>
<p>&nbsp;</p>
<p><span>80386</span><span>的中断和异常</span></p>
<p><span>中断指当程序执行过程中有更重要的事情需要实时处理时（如串口中有数据到达，不及时处理数据会丢失，串行控制器就提交一个中断信号给处理器要求处理），硬件通过中断控制器通知处理器。处理器暂时挂起当前运行的程序，转移到中断处理程序中；当中断处理程序处理完毕后，通过</span><span>iret</span><span>指令回到原先被打断的程序中继续执行。</span></p>
<p>&nbsp;</p>
<p><span>异常指指令执行中发生不可忽略的错误时（如遇到无效的指令编码，除法指令除零等），处理器用和中断处理相同的操作方法挂起当前运行的程序转移到异常处理程序中。异常处理程序决定在修正错误后是否回到原来的地方继续执行。</span></p>
<p>&nbsp;</p>
<p><span>更为</span><span>DOS</span><span>汇编程序员熟悉的中断，指的是用</span><span>int n</span><span>指令直接转移到中断向量</span><span>n</span><span>指定的中断处理程序中执行。严格地讲，</span><span>int n</span><span>指令应该算自陷而不是中断。因为这时并不是程序被急需解决的事情打断。而是自己要求停止执行并转移到中断处理程序中去。</span></p>
<p>&nbsp;</p>
<p><span>不管中断、异常还是自陷，虽然它们产生的原因不同，但处理过程是类似的，都通过中断向量表里存放的入口地址转移到服务程序，都由</span><span>CPU</span><span>自动在堆栈中保护断点地址，最后也都可以用</span><span>iret</span><span>指令返回指令被中断的地方。</span></p>
<p>&nbsp;</p>
<p><span>8086</span><span>或</span><span>80386</span><span>实模式下中断和异常的处理过程。</span></p>
<p><span>中断和异常服务程序地址存放在中断向量表中。中断向量表位于物理内存</span><span>00000h</span><span>开始的</span><span>400h</span><span>字节中，共支持</span><span>100h</span><span>个中断向量；每个中断向量是一个</span><span>xxxx:yyyy</span><span>格式的地址，占用</span><span>4</span><span>字节。当发生</span><span>n</span><span>号异常或</span><span>n</span><span>号中断，或者执行到</span><span>int n</span><span>指令的时候，</span><span>CPU</span><span>首先到内存</span><span>n 4</span><span>的地方取出服务程序的地址</span><span>aaaa:bbbb</span><span>；然后将标志寄存器、中断时的</span><span>CS</span><span>和</span><span>IP</span><span>压入堆栈，接着转移到</span><span>aaaa:bbbb</span><span>处执行；在服务程序最后遇到</span><span>iret</span><span>的时候，</span><span>CPU</span><span>从堆栈中标志寄存器，然后取出</span><span>CS</span><span>和</span><span>IP</span><span>并返回。</span></p>
<p>&nbsp;</p>
<p><span>在保护模式下，中断或异常处理往往从用户代码切换到操作系统代码中执行。由于保护模式下的代码有优先级之分，因此出现了从优先级低的应用程序转移到优先级高的系统代码中的问题，如果优先级低的代码能够任意调用优先级高的代码，就相当于拥有了高优先级代码的权限。<strong>为了使高优先级的代码能够安全地被低优先级的代码调用，保护模式下增加了门的概念。</strong></span></p>
<p>&nbsp;</p>
<p><span>门，指向某个优先级高的程序所规定的入口点，所有优先级低的程序调用优先级高的程序只能通过门重定向，进入门的规定的入口点。这样可以避免低级别的程序代码从任意位置进入优先级高的程序的问题。</span></p>
<p>&nbsp;</p>
<p><span>保护模式下的中断和异常等服务程序也要从门进入，</span><span>80386</span><span>的门分为中断门、自陷门和任务门几种。</span></p>
<p>&nbsp;</p>
<p><span>在保护模式下要表示一个中断或异常服务程序的信息需要用</span><span>8</span><span>个字节，包括门的种类以及</span><span>xxxx:yyyyyyyy</span><span>格式的入口地址等。这组信息叫做中断描述符。这样，中断向量表就无法采用和实模式下同样的</span><span>4</span><span>字节一组的格式。</span></p>
<p><span>保护模式下把所有的中断描述符放在一起组成&#8220;中断描述符表&#8221;</span><span>IDT</span><span>（</span><span>Interrupt Descriptor Table</span><span>）。</span><span>IDT</span><span>不再放在固定的地址</span><span>00000h</span><span>处，而是采用可编程设置的方式，支持的中断数量也可以设置。为此</span><span>80386</span><span>处理器引入了一个新的</span><span>48</span><span>位寄存器</span><span>IDTR</span><span>。</span><span>IDTR</span><span>的高</span><span>32</span><span>位指定了</span><span>IDT</span><span>在内存中的基址（线性地址），低</span><span>16</span><span>位指定了</span><span>IDT</span><span>的长度，相当于指定了可以支持的中断数量。</span></p>
<p>&nbsp;</p>
<p><span>保护模式下发生异常或中断时，处理器先根据</span><span>IDTR</span><span>寄存器得到中断描述符的地址，然后取出</span><span>n</span><span>号中断</span><span>/</span><span>异常的门描述符，再从描述符中得到中断服务程序的地址</span><span>xxxx:yyyyyyyy</span><span>，经过段地址转换后得到服务程序的</span><span>32</span><span>位线性地址并转移后执行。</span></p>
<p>&nbsp;</p>
<p><span>由于保护模式下用中断门可以从低优先级的代码调用高优先级的代码，所以不能让用户程序写中断描述符表，否则会引发安全问题（</span><span>CIH</span><span>病毒）。这样就如同关了窗子挡住苍蝇，也挡住了微风，用户的系统扩展程序也就不能像在</span><span>DOS</span><span>中一样再用中断服务程序的方式提供服务了。因为用户程序根本没有权限将中断地址指到自己的代码中来。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>在</span><span>Windows</span></strong><strong><span>中，操作系统使用动态链接库来代替中断服务程序提供系统功能，所以</span><span>Win32</span></strong><strong><span>汇编中</span><span>int</span></strong><strong><span>指令也就失去了存在的意义。这就是在</span><span>Win32</span></strong><strong><span>汇编源代码中看不到</span><span>int</span></strong><strong><span>指令的原因。其实那些调用</span><span>API</span></strong><strong><span>的指令原本是用</span><span>int</span></strong><strong><span>指令实现的。</span></strong></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>80386</span><span>的保护机制</span></p>
<p><span>80286</span><span>之前的处理器只支持单任务，操作系统并没有什么安全性可言，计算机的全部资源包括操作系统的内部都可以任凭程序员调用。</span></p>
<p><span>但对于多任务的操作系统，某个捣乱的程序的为所欲为令即可使所有程序都无法运行。</span></p>
<p><span>所以</span><span>80286</span><span>及以上的处理器引入了优先级的概念。</span><span>80386</span><span>处理器共设置</span><span>4</span><span>个优先级（</span><span>0~3</span><span>）。</span><span>0</span><span>级是最高级（特权级）；</span><span>3</span><span>级是最低级（用户级）；</span><span>1</span><span>级和</span><span>2</span><span>级介于它们之间。特权级代码一般是操作系统的代码，可以访问全部系统资源；其他级别的代码一般都是用户程序，可以访问的资源受到限制。</span></p>
<p>&nbsp;</p>
<p><span>80386</span><span>采用保护机制主要为了检查和防止低级别代码的越权操作，如访问不该访问的数据、端口以及调用高优先级的代码等。保护机制主要由下列几方面组成：</span></p>
<p><span>1</span><span>）段的类型检查——段的类型是由段描述符指定的，主要属性有是否可执行，是否可读和是否可写等。而</span><span>CS</span><span>、</span><span>DS</span><span>和</span><span>SS</span><span>等段选择器是否能装入某种类型的段描述符是有限制的。如不可执行的段不能装入</span><span>CS</span><span>；不可读的段不能装入</span><span>DS</span><span>与</span><span>ES</span><span>等数据段寄存器；不可写的段不能装入</span><span>SS</span><span>等。如果段类型检查通不过，则处理器会产生一般性保护异常或堆栈异常。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>）页的类型检查——除了可以在段级别上指定整个段是否可以读写外，在页表中也可以为每个页指定是否可写。对特权级下的执行代码，所有的页都是可写的。但对</span><span>1</span><span>，</span><span>2</span><span>和</span><span>3</span><span>级的代码，还要根据页表中的</span><span>R/W</span><span>项决定是否可写，企图对只读的页进行写操作会产生页异常。</span></p>
<p>&nbsp;</p>
<p><span>3</span><span>）访问数据时的级别检查——优先级低的代码不能访问优先级高的数据段。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>80386</span><span>的段描述符中有一个</span><span>DPL</span><span>域（描述符优先级），表示这个段可以被访问的最低优先级。而段选择器中含有</span><span>RPL</span><span>域（请求优先级），表示当前执行代码的优先级。只有</span><span>DPL</span><span>在数值上大于或等</span><span>RPL</span><span>值的时候，该段才是可以访问的，否则会产生一般性保护异常。</span></p>
<p>&nbsp;</p>
<p><span>4</span><span>）控制转移的检查——在处理器中，有很多指令可以实现控制转移，如</span><span>jmp</span><span>，</span><span>call</span><span>，</span><span>ret</span><span>，</span><span>int</span><span>和</span><span>iret</span><span>等指令。但优先级低的代码不能随意转移到优先级高的代码中，所以遇到这些指令的时候，处理器要检查转移的目的位置是否合法。</span></p>
<p>&nbsp;</p>
<p><span>5</span><span>）指令集的检查——有两类指令可以影响保护机制。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>第一类是改变</span><span>GDT</span><span>，</span><span>LDT</span><span>，</span><span>IDT</span><span>以及控制寄存器等关键寄存器的指令，称为特权指令；</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>第二类是操作</span><span>I/O</span><span>端口的指令以及</span><span>cli</span><span>和</span><span>sti</span><span>等改变中断允许的指令，称为敏感指令。试想一下，如果用户程序可以用</span><span>sti</span><span>禁止一切中断（包括时钟中断），那么整个系统就无法正常运行，所以这些指令的运行要受到限制。特权指令只能在优先级</span><span>0</span><span>上才能运行，而敏感指令取决于</span><span>eflags</span><span>寄存器中的</span><span>IOPL</span><span>位。只有</span><span>IOPL</span><span>位表示的优先级高于当前代码段的优先级时，指令才能执行。</span></p>
<p>&nbsp;</p>
<p><span>6</span><span>）</span><span>I/O</span><span>操作的保护——</span><span>I/O</span><span>地址也是受保护的对象。因为通过</span><span>I/O</span><span>操作可以绕过系统对很多硬件进行控制。</span><span>80386</span><span>可以单独为</span><span>I/O</span><span>空间提供保护，每个任务有个</span><span>TSS</span><span>（任务状态段）来记录任务切换的信息。</span><span>TSS</span><span>中有个</span><span>I/O</span><span>允许位图，用来表示对应的</span><span>I/O</span><span>端口是否可以操作。某个</span><span>I/O</span><span>地址在位图中的对应数据位为</span><span>0</span><span>则表示可以操作；如果为</span><span>1</span><span>则还要看</span><span>eflags</span><span>中的</span><span>IPOL</span><span>位，这时只有</span><span>IOPL</span><span>位表示的优先级高于等于当前代码段的优先级，才允许访问该</span><span>I/O</span><span>端口。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>Windows</span><span>的保护机制</span></p>
<p><span>在</span><span>Windows</span><span>下，操作系统运行于</span><span>0</span><span>行，应用程序运行于</span><span>3</span><span>级。因为</span><span>Alpha</span><span>计算机只支持两个优先级，为了便于将应用程序移植到</span><span>Alpha</span><span>计算机上，</span><span>Windows</span><span>操作系统不使用</span><span>1</span><span>和</span><span>2</span><span>级这两个优先级。</span></p>
<p>&nbsp;</p>
<p><span>Windows</span><span>操作系统充分利用</span><span>80386</span><span>的保护机制，所有和操作系统密切相关的东西都是受保护的。运行于优先级</span><span>3</span><span>上的用户程序有很多限制，只有在写</span><span>VxD</span><span>等驱动程序的时候才可以使用全部资源。在</span><span>Win32</span><span>汇编编程中要注意避免以下的越权操作（当然写驱动程序不在此列）：</span></p>
<p>&nbsp;</p>
<p><span>1</span><span>）显而易见，所有的特权指令都是不可执行的，如</span><span>lgdt</span><span>，</span><span>lldt</span><span>，</span><span>lidt</span><span>指令和对</span><span>CRx</span><span>与</span><span>TRx</span><span>等寄存器赋值。但是，读取重要寄存器的指令是可以执行的，如</span><span>sgdt</span><span>，</span><span>sldt</span><span>和</span><span>sidt</span><span>等。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>）</span><span>Windows</span><span>在页表中把代码段和数据段中的内存赋予不同的属性。代码段是不可写的，数据段中也只有变量部分的页面是可写的。所以虽然可以寻址所有的</span><span>4GB</span><span>空间，但访问越出权限规定以外的东西还是会引发保护异常。</span></p>
<p>&nbsp;</p>
<p><span>3</span><span>）在</span><span>Windows 98</span><span>中，系统硬件用的</span><span>I/O</span><span>端口是受保护的，但其余的则可以操作。如果用户在机器中插入一块自己的卡，用的是</span><span>300h</span><span>系统未定义的端口，那么在应用程序中就可以直接操作，但要操作</span><st1:chmetcnv w:st="on" UnitName="F" SourceValue="3" HasSpace="False" Negative="False" NumberType="1" TCSC="0"><span>3f</span></st1:chmetcnv><span>8h</span><span>（串口）和</span><st1:chmetcnv w:st="on" UnitName="F" SourceValue="1" HasSpace="False" Negative="False" NumberType="1" TCSC="0"><span>1f</span></st1:chmetcnv><span>0h</span><span>（硬盘端口）等系统已定义的端口就不行了。在</span><span>Windows NT</span><span>中，任何的端口操作都是不允许的。</span></p>
<p>&nbsp;</p>
<p><span>如果违反了</span><span>Windows</span><span>规定的&#8220;保护条例&#8221;，那么会引发保护异常，处理器会毫不犹豫地把控制权转移到对应的异常处理程序中去。</span><span>Windows</span><span>会在处理程序中用一个很酷的&#8220;非法操作&#8221;对话框把用户的程序判死刑，没有一点回旋的余地！在</span><span>Windows 9x</span><span>中，系统有时会用一个蓝屏幕来通知用户程序试图访问不存在的内存页。</span></p>
<p>&nbsp;</p>
<p><span>如果程序调用的</span><span>DLL</span><span>中有错，那么错误还是会算在应用程序头上，因为</span><span>DLL</span><span>的地址空间是被映射到应用程序的空间中去的。</span><span>Windows 9x</span><span>本身是</span><span>32</span><span>位和</span><span>16</span><span>位混合的操作系统，为了兼容</span><span>DOS</span><span>和</span><span>Win16</span><span>程序，很多的保护措施做起来力不从心。所以系统内部反而常常出现越权操作，以至于蓝屏幕不断，这些就不是用户应用程序自己的问题了。</span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122435.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-06 13:42 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/06/122435.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>手工打造Win32汇编开发环境</title><link>http://www.cppblog.com/luqingfei/archive/2010/07/11/120080.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Sun, 11 Jul 2010 05:01:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/07/11/120080.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/120080.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/07/11/120080.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/120080.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/120080.html</trackback:ping><description><![CDATA[<p>开发工具基于MASM32 V8<br>编辑工具：EditPlus</p>
<p>直接解压缩到指定目录，比如D盘根目录：<br>d:\masm32\<br>d:\editplus\</p>
<p>可把批处理文件var.bat放置在masm32目录下。</p>
<p>批处理文件Var.bat用于设置临时环境变量，并且调用Makefile，运行编译后的可执行文件。</p>
<p>EditPlus已经配置了ASM代码高亮及模板。</p>
<p>在EditPlus的配置用户工具中，依照如下设置，即可以在Editplus中编译ASM代码文件，并运行编译后的可执行文件。<br>菜单文本：Run Asm<br>命令：d:\masm32\var.bat<br>参数：$(FileNameNoExt)<br>初始目录：$(FileDir)</p>
<p><br>开手你的第一个ASM练习:<br>step1: 打开EditPlus<br>step2: 点击菜单[文件]，[新建 ASM] ，然后EditPlus会自动加载一个hello world的ASM模板<br>step3:如果不想加点什么，可直接保存该代码文件。比如命名为HelloAsm<br>step4：在HelloAsm文件同目录下，建立makefile文件，内容如下：<br>&nbsp;NAME = HelloAsm<br>OBJS = $(NAME).obj</p>
<p>LINK_FLAG = /subsystem:windows<br>ML_FLAG = /c /coff</p>
<p>$(NAME).exe: $(OBJS)<br>&nbsp;Link $(LINK_FLAG) $(OBJS)<br>.asm.obj:<br>&nbsp;ml $(ML_FLAG) $&lt;</p>
<p>clean:<br>&nbsp;del *.obj<br>&nbsp;<br>&nbsp;<br>step5：执行EditPlus菜单[工具]下的RunAsm命令。</p>
<p>即可以编译HelloAsm文件，并且执行编译后的可执行文件。</p>
<p>这只是个简单的粗糙的ASM IDE开发环境。<br>主要用于新手入门。</p>
<p>感谢你的使用。<br></p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/120080.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-07-11 13:01 <a href="http://www.cppblog.com/luqingfei/archive/2010/07/11/120080.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>