﻿<?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++博客-弱水一瓢的博客-文章分类-MFC</title><link>http://www.cppblog.com/Raycruiser/category/6335.html</link><description>    ——C++夜未眠</description><language>zh-cn</language><lastBuildDate>Sat, 23 Aug 2008 09:05:00 GMT</lastBuildDate><pubDate>Sat, 23 Aug 2008 09:05:00 GMT</pubDate><ttl>60</ttl><item><title>SetWindowPos</title><link>http://www.cppblog.com/Raycruiser/articles/45354.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 25 Mar 2008 07:53:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/45354.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/45354.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/45354.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/45354.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/45354.html</trackback:ping><description><![CDATA[<div class=box2><span class=Tit>SetWindowPos</span></div>
<table class="htb wr" cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>
            <div class="box2 p14">函数功能：该函数改变一个子窗口，弹出式窗口式顶层窗口的尺寸，位置和Z序。子窗口，弹出式窗口，及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高，并且被设置为Z序的第一个窗口。<br><br>函数原型：BOOL SetWindowPos（HWN hWnd，HWND hWndlnsertAfter,int X，int Y,int cx，int cy,UNIT．Flags）；<br><br>参数：<br><br>hWnd:窗口句柄。<br><br>hWndlnsertAfter:在z序中的位于被置位的窗口前的窗口句柄。该参数必须为一个窗口句柄，或下列值之一：<br><br>HWND_BOTTOM：将窗口置于Z序的底部。如果参数hWnd标识了一个顶层窗口，则窗口失去顶级位置，并且被置在其他窗口的底部。<br><br>HWND_DOTTOPMOST：将窗口置于所有非顶层窗口之上（即在所有顶层窗口之后）。如果窗口已经是非顶层窗口则该标志不起作用。<br><br>HWND_TOP:将窗口置于Z序的顶部。<br><br>HWND_TOPMOST:将窗口置于所有非顶层窗口之上。即使窗口未被激活窗口也将保持顶级位置。<br><br>查g看该参数的使用方法，请看说明部分。<br><br>x：以客户坐标指定窗口新位置的左边界。<br><br>Y：以客户坐标指定窗口新位置的顶边界。<br><br>cx:以像素指定窗口的新的宽度。<br><br>cy：以像素指定窗口的新的高度。<br><br>uFlags:窗口尺寸和定位的标志。该参数可以是下列值的组合：<br><br>SWP_ASNCWINDOWPOS：如果调用进程不拥有窗口，系统会向拥有窗口的线程发出需求。这就防止调用线程在其他线程处理需求的时候发生死锁。<br><br>SWP_DEFERERASE：防止产生WM_SYNCPAINT消息。<br><br>SWP_DRAWFRAME：在窗口周围画一个边框（定义在窗口类描述中）。<br><br>SWP_FRAMECHANGED：给窗口发送WM_NCCALCSIZE消息，即使窗口尺寸没有改变也会发送该消息。如果未指定这个标志，只有在改变了窗口尺寸时才发送WM_NCCALCSIZE。<br><br>SWP_HIDEWINDOW;隐藏窗口。<br><br>SWP_NOACTIVATE：不激活窗口。如果未设置标志，则窗口被激活，并被设置到其他最高级窗口或非最高级组的顶部（根据参数hWndlnsertAfter设置）。<br><br>SWP_NOCOPYBITS：清除客户区的所有内容。如果未设置该标志，客户区的有效内容被保存并且在窗口尺寸更新和重定位后拷贝回客户区。<br><br>SWP_NOMOVE：维持当前位置（忽略X和Y参数）。<br><br>SWP_NOOWNERZORDER：不改变z序中的所有者窗口的位置。<br><br>SWP_NOREDRAW:不重画改变的内容。如果设置了这个标志，则不发生任何重画动作。适用于客户区和非客户区（包括标题栏和滚动条）和任何由于窗回移动而露出的父窗口的所有部分。如果设置了这个标志，应用程序必须明确地使窗口无效并区重画窗口的任何部分和父窗口需要重画的部分。<br><br>SWP_NOREPOSITION；与SWP_NOOWNERZORDER标志相同。<br><br>SWP_NOSENDCHANGING：防止窗口接收WM_WINDOWPOSCHANGING消息。<br><br>SWP_NOSIZE：维持当前尺寸（忽略cx和Cy参数）。<br><br>SWP_NOZORDER：维持当前Z序（忽略hWndlnsertAfter参数）。<br><br>SWP_SHOWWINDOW：显示窗口。<br><br>返回值：如果函数成功，返回值为非零；如果函数失败，返回值为零。若想获得更多错误消息，请调用GetLastError函数。<br><br>备注：如果设置了SWP_SHOWWINDOW和SWP_HIDEWINDOW标志，则窗口不能被移动和改变大小。如果使用SetWindowLoog改变了窗口的某些数据，则必须调用函数SetWindowPos来作真正的改变。使用下列的组合标志：SWP_NOMOVEISWP_NOSIZEISWP_FRAMECHANGED。<br><br>有两种方法将窗口设为最顶层窗口：一种是将参数hWndlnsertAfter设置为HWND_TOPMOST并确保没有设置SWP_NOZORDER标志；另一种是设置窗口在Z序中的位置以使其在其他存在的窗口之上。当一个窗口被置为最顶层窗口时，属于它的所有窗口均为最顶层窗口，而它的所有者的z序并不改变。<br><br>如果HWND_TOPMOST和HWND_NOTOPMOST标志均未指定，即应用程序要求窗口在激活的同时改变其在Z序中的位置时，在参数hWndinsertAfter中指定的值只有在下列条件中才使用：<br><br>在hWndlnsertAfter参数中没有设定HWND_NOTOPMOST和HWND_TOPMOST标志。<br><br>由hWnd参数标识的窗口不是激活窗口。<br><br>如果未将一个非激活窗口设定到z序的顶端，应用程序不能激活该窗口。应用程序可以无任何限制地改变被激活窗口在Z序中的位置，或激活一个窗口并将其移到最高级窗口的顶部或非最高级窗口的顶部。<br><br>如果一个顶层窗口被重定位到z序的底部（HWND_BOTTOM）或在任何非最高序的窗口之后，该窗口就不再是最顶层窗口。当一个最顶层窗口被置为非最顶级，则它的所有者窗口和所属者窗口均为非最顶层窗口。<br><br>一个非最顶端窗口可以拥有一个最顶端窗口，但反之则不可以。任何属于顶层窗口的窗口（例如一个对话框）本身就被置为顶层窗口，以确保所有被属窗口都在它们的所有者之上。<br><br>如果应用程序不在前台，但应该位于前台，就应调用SetForegroundWindow函数来设置。<br><br>Windows CE：如果这是一个可见的顶层窗口，并且未指定SWP_NOACTIVATE标志，则这个函数将激活窗口、如果这是当前的激活窗口，并且指定了SWP_NOACTIVATE或SWP_HIDEWINDOW标志，则激活另外一个可见的顶层窗口。<br><br>当在这个函数中的nFlags参数里指定了SWP_FRAMECHANGED标志时，WindowsCE重画窗口的整个非客户区，这可能会改变客户区的大小。这也是重新计算客户区的唯一途径，也是通过调用SetwindowLong函数改变窗口风格后通常使用的方法。<br><br>SetWindowPos将使WM_WINDOWPOSCHANGED消息向窗口发送，在这个消息中传递的标志与传递给函数的相同。这个函数不传递其他消息。</div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/45354.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-25 15:53 <a href="http://www.cppblog.com/Raycruiser/articles/45354.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>树型视的三个结构TVINSERTSTRUCT、TVITEM、NMTREEVIEW</title><link>http://www.cppblog.com/Raycruiser/articles/45352.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 25 Mar 2008 07:43:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/45352.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/45352.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/45352.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/45352.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/45352.html</trackback:ping><description><![CDATA[<span>&nbsp;
<h1>TVINSERTSTRUCT</h1>
<br clear=all>
<p>包含添加新项到树形视控件所使用的信息。这个结构被TVM_INSERTITEM消息使用。这个结构与TV_INSERTSTRUCT结构是一样的，但它已经按当前的命名习惯重命名了。</p>
<pre><code>typedef struct tagTVINSERTSTRUCT {
HTREEITEM hParent;
HTREEITEM hInsertAfter;
#if (_WIN32_IE &gt;= 0x0400)
union
{
TVITEMEX itemex;
TVITEM item;
} DUMMYUNIONNAME;
#else
TVITEM item;
#endif
} TVINSERTSTRUCT, FAR *LPTVINSERTSTRUCT;
</code></pre>
<h4>成员</h4>
<dl>
<dt>hParent
<dd>父项的句柄。如果这个成员的值是TVI_ROOT或NULL，这项将被作为树形控件的根插入。
<dt>hInsertAfter
<dd>插入的新项之后的项的句柄。或是下列值之一：
<table>
    <tbody>
        <tr>
            <th>值</th>
            <th>意味</th>
        </tr>
        <tr>
            <td>TVI_FIRST</td>
            <td>在列表的开始插入项</td>
        </tr>
        <tr>
            <td>TVI_LAST</td>
            <td>在列表的最后插入项</td>
        </tr>
        <tr>
            <td>TVI_ROOT</td>
            <td>作为一个根项添加</td>
        </tr>
        <tr>
            <td>TVI_SORT</td>
            <td>以字母顺序插入项</td>
        </tr>
    </tbody>
</table>
<dt>itemex
<dd>版本4.71。TVITEMEX包含关于项添加的信息。
<dt>item
<dd>TVITEM包含关于项添加的信息。 </dd></dl>
<h4>需求</h4>
<p>&nbsp;&nbsp; Windows NT/2000：需要Windows NT 3.51或更高版本。 <br>&nbsp;&nbsp; Windows 95/98：需要Windows 95或更高版本。 <br>&nbsp;&nbsp; Header：定义在commctrl.h。</p>
<h1>TVITEM</h1>
<p>指定或接收树形视项的属性。这个结构与TV_ITEM结构一样，但它已经被当前命名协议重新命名了。新的应用程序应该使用这个结构。</p>
<pre><code>
typedef struct tagTVITEM{
UINT      mask;
HTREEITEM hItem;
UINT      state;
UINT      stateMask;
LPTSTR    pszText;
int       cchTextMax;
int       iImage;
int       iSelectedImage;
int       cChildren;
LPARAM    lParam;
} TVITEM, FAR *LPTVITEM;
</code></pre>
<h4>成员</h4>
<dl>
<dt>mask
<dd>指出其它的结构成员哪些包含有效数据的标记数组。当这个结构被TVM_GETITEM消息使用时，mask成员指出项的属性被取回。这个成员可以是下列值的一个或多个。
<table>
    <tbody>
        <tr>
            <td>TVIF_CHILDREN</td>
            <td>cChildren成员是有效的。</td>
        </tr>
        <tr>
            <td>TVIF_DI_SETITEM</td>
            <td>树形视控件将保留支持信息并且不重新请求它。当处理TVN_GETDISPINF通知时，这个标记是有效的。</td>
        </tr>
        <tr>
            <td>TVIF_HANDLE</td>
            <td>hItem成员有效。</td>
        </tr>
        <tr>
            <td>TVIF_IMAGE</td>
            <td>iImage成员有效。</td>
        </tr>
        <tr>
            <td>TVIF_PARAM</td>
            <td>lParam成员有效。</td>
        </tr>
        <tr>
            <td>TVIF_SELECTEDIMAGE</td>
            <td>iSelectedImage成员有效。</td>
        </tr>
        <tr>
            <td>TVIF_STATE</td>
            <td>state和stateMask成员有效。</td>
        </tr>
        <tr>
            <td>TVIF_TEXT</td>
            <td>pszText和cchTextMax成员有效。</td>
        </tr>
    </tbody>
</table>
<dt>hItem
<dd>这个函数引用的项。
<dt>state
<dd>位标记和图像列表索引的设置，指出项的状态。当设置了一个项的状态，stateMask成员指出这个成员的位是有效的。当取加一个项的状态时，这个成员返回stateMask成员指出的位的当前状态。
<p>这个成员的0至7位包含了项的状态标记。关于可能的项状态标记，参见Tree View Control Item States.</p>
<p>覆盖图像覆盖在项的图标图像之上。这个成员的8至11位指定了以1为基准的覆盖图像索引。如果这些位是0，这个项没有覆盖图像。要隔离这些位，使用TVIS_OVERLAYMASK掩码。要在这个成员中设置覆盖图像索引，使用INDEXTOOVERLAYMASK宏。图像列表的覆盖图像是被ImageList_SetOverlayImage函数设置的。</p>
<p>一个状态图像是仅次于指出应用程序定义的状态的项的图标显示的。通过发送TVM_SETIMAGELIST消息来指定一个状态图像列表。要设置一个项的状态图像，在TVITEM结构的stateMask成员中包含TVIS_STATEIMAGEMASK值。结构的state成员的12至15位指定状态图像列表中被绘制图像的索引。</p>
<p>要设置状态图像索引，使用INDEXTOSTATEIMAGEMASK。这个宏把一个索引适当的设置到12至15位上。要指出项没有状态图像，设置索引为0。这意味着在状态图像列表中的图像0不能被作为一个状态图像使用。要隔离state成员的位12至15，使用TVIS_STATEIMAGEMASK掩码。</p>
<dt>stateMask
<dd>state成员的位是有效的。如果你取回了一个项的状态，设置stateMask成员的位来指出state成员中的这个位被返回。如果你设置了一个项的状态，设置stateMask成员的位来指出state成员的这个位是你想设置的。要设置或取回一个项的覆盖图像的索引，设置TVIS_OVERLAYMASK位。要设置和取回一个项的状态图像索引，设置TVIS_STATEIMAGEMASK位。
<dt>pszText
<dd>如果这个结构指定了项属性，那么这个成员是指向一个以空字符结束的字符串，包含有项的文本。如果这个成员是值LPSTR_TEXTCALLBACK，那么父窗口为保存名字负责。既然这样，当树形视控件需要显示、保存或编辑项文本时，向父窗口发送TVN_GETDISPINFO通过消息，当项文本改变时，发送TVN_SETDISPINFO通知消息。
<p>如果结构是取回项的属性，这个成员是取回项文本缓冲的地址。</p>
<dt>cchTextMax
<dd>pszText成员指定缓冲的大小，以字符为单位。如果这个结构被使用来设置项属性，这个成员被忽略。
<dt>iImage
<dd>当项是在非选择状态中时，是树形控件的图像列表的索引。
<p>如果这个成员是值I_IMAGECALLBACK，父窗口为保存索引负责。既然这样，当树形视控件需要显示这个图像时，向父窗口发送TVN_GETDISPINFO通知消息来获得索引。</p>
<dt>iSelectedImage
<dd>当项被选择时，是树形控件图像列表的索引。
<p>如果这个成员是值I_IMAGECALLBACK，父窗口为保存索引负责。既然这样，当树形视控件需要显示这个图像时，向父窗口发送TVN_GETDISPINFO通知消息来获得索引。</p>
<dt>cChildren
<dd>标记指出哪一个项有关联的子项。这个成员可以是下列值之一。
<table>
    <tbody>
        <tr>
            <td>zero</td>
            <td>这个项没有子项。</td>
        </tr>
        <tr>
            <td>one</td>
            <td>这个项有一个或更多的子项。</td>
        </tr>
        <tr>
            <td>I_CHILDRENCALLBACK</td>
            <td>The parent window keeps track of whether the item has child items. In this case, when the tree view control needs to display the item, the control sends the parent a TVN_GETDISPINFO notification message to determine whether the item has child items.
            <p>If the tree view control has the TVS_HASBUTTONS style, it uses this member to determine whether to display the button indicating the presence of child items. You can use this member to force the control to display the button even though the item does not have any child items inserted. This allows you to display the button while minimizing the control's memory usage by inserting child items only when the item is visible or expanded.</p>
            </td>
        </tr>
    </tbody>
</table>
<dt>lParam
<dd>与这项相关的32位值。 </dd></dl>
<h4>需要</h4>
<p>&nbsp;&nbsp; Windows NT/2000：需要Windows NT 3.51或更高版本。 <br>&nbsp;&nbsp; Windows 95/98：需要Windows 95或更高版本。 <br>&nbsp;&nbsp; Header：定义在commctrl.h。</p>
<h1>NMTREEVIEW</h1>
<p>&#160;</p>
<p>包含关于树形视通知消息的信息。这个结构与NM_TREEVIEW结构一样，但它已经用当前的命名规则进行了重命名。</p>
<pre><code>typedef struct tagNMTREEVIEW {
NMHDR hdr;
UINT action;
TVITEM itemOld;
TVITEM itemNew;
POINT ptDrag;
} NMTREEVIEW, FAR *LPNMTREEVIEW;
</code></pre>
<h4>成员</h4>
<dl>
<dt>hdr
<dd>NMHDR结构，包含了关于这个通知消息的信息
<dt>action
<dd>通知指定的动作标记。
<dt>itemOld
<dd>包含关于旧项状态信息的TVITEM结构。通知消息没有使用它时，这个成员为0。
<dt>itemNew
<dd>包含关于新项状态信息的TVITEM结构。通知消息没有使用它时，这个成员为0。
<dt>ptDrag
<dd>包含引起通知消息发送的事件信息的POINT结构。 </dd></dl>
<h4>参见</h4>
<p>WM_NOTIFY</p>
<h4></h4>
</span>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/45352.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-25 15:43 <a href="http://www.cppblog.com/Raycruiser/articles/45352.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>窗口重绘函数心得</title><link>http://www.cppblog.com/Raycruiser/articles/45309.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Mon, 24 Mar 2008 13:48:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/45309.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/45309.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/45309.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/45309.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/45309.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在刷新窗口时经常要调用重绘函数MFC提供了三个函数用于窗口重绘<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InvalidateRect(&amp;Rect)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Invalidate()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UpdateWindow()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当需要更新或者重绘窗口时，一般系统会发出两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化)WM_NVPAINT系统会自己搞定WM_PAINT消息对应的函数是OnPaint(),它是系统默认的接受WM_PAINT消息的函数，但我们一般在程序中做重绘时都在OnDraw函数中进行的，因为在视图类ONPAINT函数中调用了ONDRAW函数。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CView默认的标准的重画函数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void CView::OnPaint()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CPaintDC dc(this);&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OnPreparDC(&amp;dc)；&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OnDraw(&amp;dc); //调用了OnDraw}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上面讲到InvalidateRect(&amp;Rect)Invalidate()两个函数形式和功能差不多但Invalidate是使得整个窗口无效，形成无效矩形，而InvalidateRect(&amp;Rect)是使得指定的区域无效 Invalidate()申明无效，等待WM_PAINT消息以便重绘，队列中无其他消息时系统会自动发送UpdateWindow()会立即发送WM_PAINT,不过在它发送前，先调用GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制区域，如果没有则不发送消息 RedrawWindow()RedrawWindow()则是具有Invalidate()和UpdateWindow()的双特性。声明窗口的状态为无效，并立即更新窗口，立即调用WM_PAINT消息处理。&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;系统为什么不在调用Invalidate时发送WM_PAINT消息呢？又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢？这是因为系统把在窗口中的绘制操作当作一种低优先级的操作，于是尽 可能地推后做。不过这样也有利于提高绘制的效率：两个WM_PAINT消息之间通过InvalidateRect和InvaliateRgn使之失效的区域就会被累加起来，然后在一个WM_PAINT消息中一次得到 更新，不仅能避免多次重复地更新同一区域，也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效，依赖于系统在合适的时机发送WM_PAINT消息的机 制实际上是一种异步工作方式，也就是说，在无效化窗口区域和发送WM_PAINT消息之间是有延迟的；有时候这种延迟并不是我们希望的，这时我们当然可以在无效化窗口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画，但不如使用Windows GDI为我们提供的更方便和强大的函数：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的 Update Region，当其不为空时才发送WM_PAINT消息；RedrawWindow则给我们更多的控制：是否重画非客户区和背景，是否总是发送 WM_PAINT消息而不管Update Region是否为空等。 BeginPaint和WM_PAINT消息紧密相关。试一试在WM_PAINT处理函数中不写BeginPaint会怎样？程序会像进入了一个死循环一样达到惊人的CPU占用率，你会发现程序总在处理一个接 一个的WM_PAINT消息。这是因为在通常情况下，当应用收到WM_PAINT消息时，窗口的Update Region都是非空的（如果为空就不需要发送WM_PAINT消息了），BeginPaint的一个作用就是把该Update Region置为空，这样如果不调用BeginPaint，窗口的Update Region就一直不为空，如前所述，系统就会一直发送WM_PAINT消息。 BeginPaint和WM_ERASEBKGND消息也有关系。当窗口的Update Region被标志为需要擦除背景时，BeginPaint会发送WM_ERASEBKGND消息来重画背景，同时在其返回信息里有一个标志表明窗口背景是否被重画过。当我们用InvalidateRect和InvalidateRgn来把指定区域加到Update Region中时，可以设置该区域是否需要被擦除背景，这样下一个BeginPaint就知道是否需要发送WM_ERASEBKGND消息了。另外要注意的一点是，BeginPaint只能在WM_PAINT处理函数中使用。 
<img src ="http://www.cppblog.com/Raycruiser/aggbug/45309.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-24 21:48 <a href="http://www.cppblog.com/Raycruiser/articles/45309.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UpdateData、Invalidate、InvalidateRect和UpdateWindow</title><link>http://www.cppblog.com/Raycruiser/articles/45306.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Mon, 24 Mar 2008 13:19:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/45306.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/45306.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/45306.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/45306.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/45306.html</trackback:ping><description><![CDATA[<p><span class=Title><strong>UpdateData():</strong></span></p>
<p>&nbsp;&nbsp;&nbsp; 当你使用了ClassWizard建立了控件和变量之间的联系后：当你修改了变量的值，而希望对话框控件更新显示，就应该在修改变量后调用UpdateData(FALSE)；如果你希望知道用户在对话框中到底输入了什么，就应该在访问变量前调用UpdateData(TRUE),将控件的输入映射到变量中。</p>
<p><strong><span class=Title><strong>Invalidate():</strong></span></strong><br>&nbsp;&nbsp;&nbsp;&nbsp; 该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘，例如，如果一个被其它窗口遮住的窗口变成了前台窗口，那么原来被遮住的部分就是无效的，需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint，OnPaint负责重绘窗口。视图类有一些例外，在视图类的OnPaint函数中调用了OnDraw函数，实际的重绘工作由OnDraw来完成。参数bErase为TRUE时，重绘区域内的背景 将被擦除，否则，背景将保持不变。</p>
<p><strong><span class=Title><strong>InvalidateRect():</strong><br></span></strong>&nbsp;&nbsp;&nbsp; 该函数的功能与Invalidate基本一样，不同的是，它是使指定的某个区域无效，需要输入一个区域。</p>
<p><strong><span class=Title><strong>UpdateWindow():</strong></span></strong><br>&nbsp;&nbsp;&nbsp;&nbsp; UpdateWindow( )的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘，这是由于WM_PAINT消息的优先级很低，它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口，从而导致窗口立即重绘。<br><br>UpdateWindow：<strong>如果有无效区，则马上sending&nbsp;a&nbsp;WM_PAINT&nbsp;message到窗口处理过程，不进消息队列进行排队等待，立即刷新窗口，否则，什么都不做。</strong> <br>&nbsp;InvalidateRect：设置无效区，如果为NULL参数，则设置整个窗口为无效区。当应用程序的那个窗口的消息队列为空时，则sending&nbsp;a&nbsp;WM_PAINT&nbsp;message(即使更新区域为空).在sending&nbsp;a&nbsp;WM_PAINT&nbsp;message的所有InvalidateRect的更新区域会累加。 <br></p>
<p align=left>&nbsp;1:设置无效区 <br>&nbsp;InvalidateRect <br><br>&nbsp;2：立即刷新 <br>&nbsp;UpdateWindow(); <br><br><strong>如果不调用&nbsp;InvalidateRect就调用&nbsp;UpdateWindow，那么UpdateWindow什么都不做。</strong> ??????<br>如果调用&nbsp;InvalidateRect&nbsp;后不调用UpdateWindow，则系统会自动在窗口消息队列为空的时候，系统自动发送一WM_PAINT消息。 <br></p>
<p align=left><strong>调用UpdateWindow()时将会发送一个WM_PAINT消息，而应用程序在接收到WM_PAINT消息后，将自动地调用Invalidate(),</strong>所以，在程序代码中，不一定要出现Invalidate()!</p>
<p align=left><strong>UpdateWindow()就是立即发送WM_PAINT消息,只对声明无效的区域起作用，</strong> &nbsp; <br>&nbsp; Invalidate()则是声明无效的方式之一。</p>
<p align=left><strong>Invalidate()表示客户区域无效</strong>，在下次WM_PAINT发生时重绘。<strong>而WM_PAINT是由系统进行维护的，每当CWnd的更新区域不为空，并且在应用程序的窗口消息队列中没有其它消息时，Windows就发送一条WM_PAINT消息</strong>。&nbsp;&nbsp;&nbsp;<br>&nbsp; Invalidate里面有个bool型的参数，用来标识重绘的时候是否用背景色填充。是不是用SetBkcolor函数？下去继续研究。</p>
<p>&nbsp;updateWindow则是要求系统对区域进行立即重绘。</p>
<p>&nbsp;看到有人在网上提出问题，他在Invalidate后面又写了绘图的函数但是没有执行，因为invalidate执行过以后转到PAINT命令了。所以后面的都没有显示。</p>
<p>&nbsp;也终于想通我绘的图一直在闪啊闪，因为我在PAINT里面用到Invalidate()函数，所以他不停的自嵌套，倒是绘的图不停的闪。</p>
<p>&nbsp;</p>
<p>Invalidate让客户区处于可以重画的状态，而UpdateWindow开始重画，但是它先判断客户区是否为空，不空UpdateWindow不执行，为空才执行重画。</p>
<p>&nbsp;</p>
<p><strong>Invalidat最后也是调用InvalidatRect</strong>,在windows API里只有InvalidatRect的</p>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/45306.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-24 21:19 <a href="http://www.cppblog.com/Raycruiser/articles/45306.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>创造你自己的控件</title><link>http://www.cppblog.com/Raycruiser/articles/44714.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Mon, 17 Mar 2008 13:33:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44714.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44714.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44714.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44714.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44714.html</trackback:ping><description><![CDATA[<p><strong><span>介绍</span></strong></p>
<p><span>作为一个程序员有许多普通的<span>windows</span>控件可以用在应用程序的外观上。许多的控件的从列表到按钮再到进程条都是可以现成的用。尽管如此，在如此多的控件中我们还是会碰到那些标准的控件不够用的时候。欢迎进入子分类控件的艺术。</span></p>
<p>&nbsp;</p>
<p><span>子分类一个<span>windows</span>控件不像子分类一个<span>C++</span>类。子分类一个控件意味这你用你自己的消息处理函数取代了改控件的一些或者所有的消息处理函数。你可以有效的截获改控件的消息并使它按照你的意愿行事，而非<span>windows</span>的默认方式。这可以让改控件实现大多数而非全部的你想得到的行为，并且使它表现得很完美。有两种类型的子分类，局部子分类和全局子分类。局部子分类就是子分类一个实体，全局子分类就是将一个特定类型的控件全部子分成你的类型。</span></p>
<p>&nbsp;</p>
<p><span>记住一个从<span>CWnd</span>类派生的类对象和一个与它相联的窗口（<span>hwnd</span>）的区别是很重要的。<span>CWnd</span>的派生类对象包含一个成员变量指向<span>hwnd</span>，而且包含那些通过<span>hwnd</span>作为参数的处理消息的函数<span>(</span>比如，<span>WM_PAINT, WM_MOUSEMOVE)</span>。当你子分类一个控件通过你的<span>C++</span>对象时，你就是将相应的<span>hwnd</span>连接到你的<span>C++</span>对象上并把改控件将激发的消息回调函数改成你的。</span></p>
<p>&nbsp;</p>
<p><span>子分类是很容易的。首先，你创建一个处理了你感兴趣的所以消息的类，然后将该类来子分一个已经存在的控件使它按照你的新类的行事。某中方面上改控件已经变成了你所拥有的了。在这个例子中我们将子分一个按钮控件并且使它做一些它从来都没能够做的事。</span></p>
<p>&nbsp;</p>
<p><strong><span>一个新类</span></strong></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>子分一个控件我们需要创建一个新类改类应该处理了所有我们感兴趣的消息。由于我们很懒，最好是使我们处理的消息最少，而且最好的方式是从你将要子分的控件派生你的新类，我们这里选择的是<span>CButton</span>。</span></p>
<p>&nbsp;</p>
<p><span>我们设想的是使按钮在鼠标每次经过时显示出高亮的黄色。奇怪的事已经产生了。首先我们通过向导创建一个从<span>CButton</span>派生的新类<span>CMyButton</span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;<img src="http://blog.programfan.com/upfile/200707/20070720205118.gif"></span></span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>通过<span>MFC</span>框架派生<span>CButton</span>类有许多优点，最大的就是我们不需要实际的为我们的类添加一行控件的代码。假如我们愿意我们可以通过我们的新类在下一步子分一个按钮控件，尽管有些烦琐。这是因为<span>MFC</span>实现了所有缺省的消息处理函数，所以我们可以简单的选择一个我们感兴趣的消息忽略其他的。</span></p>
<p><span>However for this example we have loftier plans for our control - making it bright yellow. </span></p>
<p><span>无论怎样在这个例子中我们将使我们的控件表面高亮黄色显示。</span></p>
<p>&nbsp;</p>
<p><span>为了检查鼠标是否是经过了控件我们将设置一个布尔变量<span>m_bOverControl</span>为<span>TRUE</span>当鼠标进入控件边界时，并且通过定时器实时的检查跟踪鼠标什么时候离开了控件。不幸的是我们没有平台提供的<span>OnMouseEnter </span>和<span> OnMouseLeave</span>函数可用，我们将通过<span>OnMouseMove</span>。加入我么在某个时候发现鼠标不再在控件上，我们将关闭定时器并重画该控件。</span></p>
<p><span>通过类向导添加<span>WM_MOUSEMOVE</span>和<span>WM_TIMER</span>消息处理函数<span>OnMouseMove</span>和<span>OnTimer </span>。</span></p>
<p><span><img src="http://blog.programfan.com/upfile/200707/20070720205157.gif"></span></p>
<p><span>类向导将会添加如下代码到你的新<span>button</span>类：</span></p>
<p><span>BEGIN_MESSAGE_MAP(CMyButton, CButton)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>//{{AFX_MSG_MAP(CMyButton)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>ON_WM_MOUSEMOVE()</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>ON_WM_TIMER()</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>//}}AFX_MSG_MAP</span></p>
<p><span>END_MESSAGE_MAP()</span></p>
<p>&nbsp;</p>
<p><span>/////////////////////////////////////////////////////////////////////////////</span></p>
<p><span>// CMyButton message handlers</span></p>
<p>&nbsp;</p>
<p><span>void CMyButton::OnMouseMove(UINT nFlags, CPoint point) </span></p>
<p><span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>// TODO: Add your message handler code here and/or call default</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CButton::OnMouseMove(nFlags, point);</span></p>
<p><span>}</span></p>
<p>&nbsp;</p>
<p><span>void CMyButton::OnTimer(UINT nIDEvent) </span></p>
<p><span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>// TODO: Add your message handler code here and/or call default</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CButton::OnTimer(nIDEvent);</span></p>
<p><span>}</span></p>
<p><span>消息映像的入口将消息映射成函数。<span>ON_WM_MOUSEMOVE</span>映射成函数<span>OnMouseMove</span>，<span>ON_WM_TIMER</span>映射成<span>OnTimer</span>。这些宏在<span>MFC</span>源代码中定义，但是它们不需要阅读。这个练习仅仅知道它们这样处理就可以了。</span></p>
<p><span>我们定义了两个布尔变量<span>m_bOverControl</span>和<span>m_nTimer</span>，一个<span>UNIT</span>变量，在构造函数里面初始化它们，我们的消息处理函数如下：</span></p>
<p><span>IDvoid CMyButton::OnMouseMove(UINT nFlags, CPoint point) </span></p>
<p><span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>if (!m_bOverControl)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Cursor has just moved over control</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>TRACE0("Entering control\n");</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>m_bOverControl = TRUE;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Set flag telling us the mouse is in</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Invalidate();<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>// Force a redraw</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>SetTimer(m_nTimerID, 100, NULL);<span>&nbsp;&nbsp;&nbsp; </span>// Keep checking back every 1/10 sec</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CButton::OnMouseMove(nFlags, point);<span>&nbsp;&nbsp;&nbsp; </span>// drop through to default handler</span></p>
<p><span>}</span></p>
<p>&nbsp;</p>
<p><span>void CMyButton::OnTimer(UINT nIDEvent) </span></p>
<p><span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>// Where is the mouse?</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CPoint p(GetMessagePos());</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>ScreenToClient(&amp;p);</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>// Get the bounds of the control (just the client area)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CRect rect;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>GetClientRect(rect);</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>// Check the mouse is inside the control</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>if (!rect.PtInRect(p))</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span>TRACE0("Leaving control\n");</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// if not then stop looking...</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>m_bOverControl = FALSE;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>KillTimer(m_nTimerID);</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// ...and redraw the control</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Invalidate();</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>// drop through to default handler</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CButton::OnTimer(nIDEvent);</span></p>
<p><span>}</span></p>
<p><span>我们的新类最后将要做的就是绘制，这里我们不是要处理一个消息而是重载一<span>CWnd</span>的虚方法<span>DrawItem</span>。这个方法仅仅是在自绘控件时调用，而且没有一个缺省的实现可以让你调用。（可以通过<span>ASSERT'</span>试试）这个方法被设计成仅被重载以及被派生类使用。</span></p>
<p align=center><span><img src="http://blog.programfan.com/upfile/200707/2007072020532.gif"></span></p>
<p><span>通过向导添加一个</span><font color=#990000><code><span>DrawItem</span></code><code><span>方法并且添加如下代码：</span></code></font></p>
<p><strong><span><font color=#990000></font>Collapse</span></strong></p>
<pre><font size=+0><span><span><font color=#0000ff>void</font></span></span><span> CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) </span></font></pre>
<pre><span><font size=+0>{</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>CDC* pDC<span>&nbsp;&nbsp; </span>= CDC::FromHandle(lpDrawItemStruct-&gt;hDC);</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>CRect rect = lpDrawItemStruct-&gt;rcItem;</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>UINT state = lpDrawItemStruct-&gt;itemState;</font></span></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>CString strText;</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>GetWindowText(strText);</font></span></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp; </span></span><em><font color=#008000><span><span>// draw the control edges (DrawFrameControl is handy!)</span></span></font></em></font></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp; </span></span><span><span><font color=#0000ff>if</font></span></span><span> (state &amp; ODS_SELECTED)</span></font></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pDC-&gt;DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED);</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp; </span></span><font color=#0000ff><span><span>else</span></span></font></font></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pDC-&gt;DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH);</font></span></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp; </span></span><em><font color=#008000><span><span>// Deflate the drawing rect by the size of the button's edges</span></span></font></em></font></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>rect.DeflateRect( CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)));</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span></font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><em><font color=#008000><span><span>// Fill the interior color if necessary</span></span></font></em></font></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp; </span></span><span><span><font color=#0000ff>if</font></span></span><span> (m_bOverControl)</span></font></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pDC-&gt;FillSolidRect(rect, RGB(</span><span><span><font color=#191970>255</font></span></span><span>, </span><span><span><font color=#191970>255</font></span></span><span>, </span><span><span><font color=#191970>0</font></span></span><span>)); </span><em><font color=#008000><span><span>// yellow</span></span></font></em></font></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp; </span></span><em><font color=#008000><span><span>// Draw the text</span></span></font></em></font></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp; </span></span><span><span><font color=#0000ff>if</font></span></span><span> (!strText.IsEmpty())</span></font></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>{</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CSize Extent = pDC-&gt;GetTextExtent(strText);</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CPoint pt( rect.CenterPoint().x - Extent.cx/</span><span><span><font color=#191970>2</font></span></span><span>, </span></font></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>rect.CenterPoint().y - Extent.cy/</span><span><span><font color=#191970>2</font></span></span><span> );</span></font></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span><span><font color=#0000ff>if</font></span></span><span> (state &amp; ODS_SELECTED) </span></font></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>pt.Offset(</span><span><span><font color=#191970>1</font></span></span><span>,</span><span><span><font color=#191970>1</font></span></span><span>);</span></font></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span><span><font color=#0000ff>int</font></span></span><span> nMode = pDC-&gt;SetBkMode(TRANSPARENT);</span></font></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span><span><font color=#0000ff>if</font></span></span><span> (state &amp; ODS_DISABLED)</span></font></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pDC-&gt;DrawState(pt, Extent, strText, DSS_DISABLED, TRUE, </span><span><span><font color=#191970>0</font></span></span><span>, (HBRUSH)NULL);</span></font></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><font color=#0000ff><span><span>else</span></span></font></font></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pDC-&gt;TextOut(pt.x, pt.y, strText);</font></span></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pDC-&gt;SetBkMode(nMode);</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>}</font></span></pre>
<pre><span><font size=+0>}</font></span></pre>
<p><span>所有的都已经做好了<span>-</span>进缺最后一步。<span>DrawItem</span>方法需要被绘制控件可以自绘。这可以通过在对话框编辑器里选中相应的选项实现<span>-</span>但是更好的方式是通过类自己设置样式使得该类成为真正替代<span>CButton</span>的&#8220;<span>drop-in</span>&#8221;。为了实现这点我们需要重写最后一个方法：</span><code><span><font color=#990000>PreSubclassWindow</font></span></code><span>. </span></p>
<p>&nbsp;</p>
<p><span>这个方法被</span><font color=#990000><code><span>SubclassWindow</span></code><code><span>调用，并依次被<span>CWnd::Create</span></span></code></font><span> </span><span>或者</span><font color=#990000><code><span>DDX_Control</span></code><code><span>调用，这意味着加入你动态或者通过对话框模板创建一个新类的对象，<span>PreSubclassWindow</span>仍然会被调用。<span>PreSubclassWindow</span>将在你子分的控件已经产生但是还为显示之前调用。换句话说这就是控件的一个完美的初始化时刻。</span></code></font></p>
<p><span>需要重点注意的一点是：假如你的控件是通过对话框编辑器创建，那么你子分的控件将不会有<span>WM_CREATE</span>消息，因此我们不能通过使用</span><font color=#990000><code><span>OnCreate</span></code><code><span>来初始化，因为它根本不会被调用。</span></code></font></p>
<p><font color=#990000><code><span>通过向导重载<span>PreSubclassWindow</span>并添加如下代码：</span></code></font></p>
<pre><font size=+0><span><span><font color=#0000ff>void</font></span></span><span> CMyButton::PreSubclassWindow() </span></font></pre>
<pre><span><font size=+0>{</font></span></pre>
<pre><span><font size=+0><span>&nbsp;&nbsp;&nbsp; </span>CButton::PreSubclassWindow();</font></span></pre>
<pre><span><font size=+0>&nbsp;</font></span></pre>
<pre><font size=+0><span><span>&nbsp;&nbsp;&nbsp; </span>ModifyStyle(</span><span><span><font color=#191970>0</font></span></span><span>, BS_OWNERDRAW);<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><em><font color=#008000><span><span>// make the button owner drawn</span></span></font></em></font></pre>
<pre><span><font size=+0>}</font></span></pre>
<p><span>祝贺你<span>-</span>你现在已经有一个<span>CButton</span>的派生类了！</span></p>
<h2><span><font color=#ff9900>子类</font></span></h2>
<p><strong><span>通过<span>DDX</span>在创建的时候子分一个窗口</span></strong></p>
<p><span>在这个例子中我们要子分的控件是在对话框上放置的：</span></p>
<p align=center><span><img src="http://blog.programfan.com/upfile/200707/2007072020539.gif"></span></p>
<p><span>我们让正常的对话框创建流程创建一个带有控件的对话框，然后通过<span>DDX</span>和我们的新类子分改控件。为了做到这点，我们仅仅需要通过向导添加一个控件变量使其成为对话框类的成员（在这里它的<span>ID</span>是</span><code><span><font color=#990000>IDC_BUTTON1</font></span></code><span>），类名为<span>CMyButton</span>。</span></p>
<p align=center><span><img src="http://blog.programfan.com/upfile/200707/20070720205324.gif"></span></p>
<p><span>向导在你的对话框成员方法<span>DoDataExchange</span>中产生了一个<span>DDX_Control</span>调用。<span>DDX_Control</span>会调用<span>SubclassWindow</span>使得该按钮使用<span>CMyButton</span>代替常规的<span>CButton</span>的处理方法。此按钮已经被劫持而且会按照你的设想形式了。</span></p>
<p>&nbsp;</p>
<p><strong><span>子分类一个窗口但是不被向导识别</span></strong></p>
<p><span>加入你添加一个窗口类到你的工程并且希望通过这个类的一个对象子分一个窗口，但是向导并不允许你的新类作为一个类型选项，这时你也许需要重新构建类向导文件了。</span></p>
<p><span>先备份工程中的<span>.clw</span>文件，然后删除之，接着在<span>Visual Studio</span>上按<span>CTRL+W</span>。你将会看到一个提示你哪些文件将被包含到类扫描过程中。确信你的新类文件在其中。</span></p>
<p><span>现在你的新类将可以作为一个类选项了，如果不是，你仍然可以通过类向导产生一个控件（比如说<span>CButton</span>），然后在头文件中手动修改其类名（比如<span>CMyButton</span>）。</span></p>
<p><strong><span>子分一个存在的窗口</span></strong></p>
<p><span>使用<span>DDX</span>很简单，但是它不能帮助我们子分一个已经存在的控件。比如说，你想子分一个组合框中的编辑框控件。你需要在你子分编辑框控件之前已经创建了组合框（因此它的子编辑框窗口也就创建了）。</span></p>
<p><span>在这种情况下你可以使用非常好用的<span>SubclassDlgItem </span>或者<span> SubclassWindow</span>方法。这两个方法允许你动态的子分一个窗口<span>-</span>换句话说，可以连接一个你的新窗口对象到一个已经存在的窗口。</span></p>
<p><span>例如<span>,</span>假设我们包含<span>ID</span>为</span><font color=#990000><code><span>IDC_BUTTON1</span></code><code><span>的按钮的对话框。那个按钮已经被创建了，我们希望关联一个类型为<span>CMyButton</span>的对象到那个按钮上从而使该按钮按照我们希望的方式响应。</span></code></font></p>
<p><span>为了做这些我们需要有一个已经存在的新类对象，一个对话框或者视图的成员变量是最好的。</span></p>
<p>&nbsp;</p>
<pre><span><font size=+0>CMyButton m_btnMyButton;</font></span></pre>
<p><span>接着在对话框中调用</span><font color=#990000><code><span>OnInitDialog</span></code><code><span>（或者任何恰当的地方）：</span></code></font></p>
<pre><font size=+0><span>m_btnMyButton.SubclassDlgItem(IDC_BUTTON1, </span><span><span><font color=#0000ff>this</font></span></span><span>);</span></font></pre>
<p><span>另一方面，假设你已经有一个希望子分类的指向窗口的指针或者一个从</span><code><span><font color=#990000>CView</font></span></code><span> </span><span>或其他</span><font color=#990000><code><span>CWnd</span></code><code><span>派生的类中动态创建的控件，而你不希望使用<span>SubclassDlgItem</span>，那么可以简单的调用：</span></code></font></p>
<p>&nbsp;</p>
<pre><font size=+0><span>CWnd* pWnd = GetDlgItem(IDC_BUTTON1); </span><em><font color=#008000><span><span>// or use some other method to get</span></span></font></em></font></pre>
<pre><font size=+0><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; </span></span><em><font color=#008000><span><span>// a pointer to the window you wish</span></span></font></em></font></pre>
<pre><font size=+0><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; </span></span><em><font color=#008000><span><span>// to subclass</span></span></font></em></font></pre>
<pre><span><font size=+0>ASSERT( pWnd &amp;&amp; pWnd-&gt;GetSafeHwnd() );</font></span></pre>
<pre><span><font size=+0>m_btnMyButton.SubclassWindow(pWnd-&gt;GetSafeHwnd());</font></span></pre>
<p><span>绘制按钮非常简单<span>,</span>除了不能将按钮的样式设为<span>flat</span>或者两端对齐的文本外其他任何你想要的样式都可以。当你编译运行改程序时你会看见一个简单的按钮当你的鼠标经过它时会呈现出亮黄色。</span></p>
<p align=center><span><img src="http://blog.programfan.com/upfile/200707/20070720205330.gif"></span></p>
<p><span>注意到我们仅仅真正的重载了自绘的方法，以及鼠标移动消息的处理函数，这意味这该控件仍然是下按式的按钮。给对话框类添加一个单击的处理函数你会发现它仍然可以被调用。</span></p>
<p><strong><span>结尾</span></strong></p>
<p><span>子分类并不难<span>-</span>你仅仅需要仔细选择你需要子分的类，而且要意识到你需要处理的消息函数。仔细研究你需要子分的控件<span>—</span>学习相关消息的处理函数和该类实现的虚成员方法。一旦你深入一个控件并且掌握了它的内部工作机制，你会发现一切都是那么容易！</span></p>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44714.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-17 21:33 <a href="http://www.cppblog.com/Raycruiser/articles/44714.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>改变对话框的背景颜色</title><link>http://www.cppblog.com/Raycruiser/articles/44712.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Mon, 17 Mar 2008 13:30:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44712.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44712.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44712.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44712.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44712.html</trackback:ping><description><![CDATA[<div><strong><font size=3>---- 方法一：调用CWinApp类的成员函数SetDialogBkColor来实现。</font></strong></div>
<div><strong><font size=3>---- 其中函数的第一个参数指定了背景颜色，第二个参数指定了文本颜色。下面的例子是将应用程序对话框设置为蓝色背景和红色文本，步骤如下：</font></strong></div>
<div><strong><font size=3>---- ① 新建一个基于Dialog的MFC AppWizard应用程序ExampleDlg。</font></strong></div>
<div><strong><font size=3>---- ② 在CExampleDlgApp ::InitInstance()中添加如下代码：</font></strong></div>
<div><strong><font size=3>BOOL CExampleDlgApp: : InitInstance ( )<br>{<br>...<br>&nbsp;&nbsp;&nbsp; CExampleDlgDlg dlg;<br>&nbsp;&nbsp;&nbsp; m_pMainWnd = &amp;dlg;</font></strong></div>
<div><strong><font size=3>//先于DoModal()调用，将对话框设置为蓝色背景、红色文本<br>&nbsp;&nbsp;&nbsp; SetDialogBkColor(RGB(0,0,255),RGB(255,0,0));<br>&nbsp;&nbsp;&nbsp; int nResponse = dlg.DoModal();<br>...<br>}</font></strong></div>
<div><strong><font size=3>---- 编译并运行，此时对话框的背景色和文本色已发生了改变。值得注意的是：在调用DoModal()之前必须先调用SetDialogBkColor，且此方法是将改变应用程序中所有的对话框颜色，并不能针对某一个指定的对话框。<br>---- 方法二：重载OnPaint()，即WM_PAINT消息。有关代码如下（以上例工程为准）：</font></strong></div>
<div><strong><font size=3>void CExampleDlgDlg::OnPaint()<br>{<br>&nbsp;&nbsp;&nbsp; if (IsIconic())<br>...<br>&nbsp; else<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRect rect;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CPaintDC dc(this);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetClientRect(rect);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dc.FillSolidRect(rect,RGB(0,255,0));&nbsp; //设置为绿色背景</font></strong></div>
<div><strong><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CDialog::OnPaint();<br>&nbsp; }</font></strong></div>
<div><br><strong><font size=3>---- 方法三：重载OnCtlColor (CDC* pDC, CWnd* pWnd, UINT nCtlColor)，即WM_CTLCOLOR消息。具体步骤如下（以上例工程为准）：<br>---- ①在CExampleDlgDlg的头文件中，添加一CBrush的成员变量：</font></strong></div>
<div><strong><font size=3>class CExampleDlgDlg : public CDialog<br>{<br>...<br>protected:<br>CBrush m_brush;<br>...<br>};</font></strong></div>
<div><br><strong><font size=3>---- ②在OnInitDialog()函数中添加如下代码：<br>BOOL CExampleDlgDlg::OnInitDialog()<br>{<br>...<br>// TODO: Add extra initialization here<br>m_brush.CreateSolidBrush(RGB(0, 255, 0)); // 生成一绿色刷子<br>...<br>}</font></strong></div>
<div><strong><font size=3>---- ③利用ClassWizard重载OnCtlColor(...)，即WM_CTLCOLOR消息：<br>HBRUSH CExampleDlgDlg::OnCtlColor<br>(CDC* pDC, CWnd* pWnd, UINT nCtlColor)<br>{<br>/*<br>** 这里不必编写任何代码！<br>**下行代码要注释掉<br>** HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);<br>*/</font></strong></div>
<div><strong><font size=3>return m_brush;&nbsp; //返加绿色刷子<br>}</font></strong></div>
<div><br><strong><font size=3>---- 方法四：还是重载OnCtlColor (CDC* pDC, CWnd* pWnd, UINT nCtlColor)，即WM_CTLCOLOR消息。具体步骤如下（以上例工程为准）：<br>---- 步骤①、②同上方法三中的步骤①、②。</font></strong></div>
<div><strong><font size=3>---- 步骤③利用ClassWizard重载OnCtlColor(...)（即WM_CTLCOLOR消息）时则有些不同：</font></strong></div>
<div><strong><font size=3>HBRUSH CExampleDlgDlg::OnCtlColor<br>(CDC* pDC, CWnd* pWnd, UINT nCtlColor)<br>{<br>HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);</font></strong></div>
<div><strong><font size=3>//在这加一条是否为对话框的判断语句<br>if(nCtlColor ==CTLCOLOR_DLG)<br>return m_brush;&nbsp; //返加绿色刷子<br>return hbr;<br>}</font></strong></div>
<div><strong><font size=3>---- 编译并运行即可。</font></strong></div>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44712.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-17 21:30 <a href="http://www.cppblog.com/Raycruiser/articles/44712.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>文件操作API和CFile类</title><link>http://www.cppblog.com/Raycruiser/articles/44581.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Sat, 15 Mar 2008 10:19:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44581.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44581.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44581.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44581.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44581.html</trackback:ping><description><![CDATA[<p>在VC中，操作文件的方法有两种，一是利用一些API函数来创建，打开，读写文件，另外一个是利用MFC的CFile类，CFile封装了对文件的一般操作。下面主要介绍如何利用这两种方法操作文件。<br>一．创建或打开一个文件</p>
<p>1、 API函数CreateFile可打开和创建文件、管道、邮槽、通信服务、设备以及控制台，但是在此时只是介绍用这个函数怎么实现创建和打开一个文件。<br>HANDLE CreateFile(<br>LPCTSTR lpFileName, // 要打开的文件名<br>DWORD dwDesiredAccess, // 文件的操作属性<br>DWORD dwShareMode, // 文件共享属性<br>LPSECURITY_ATTRIBUTES lpSecurityAttributes,// 文件安全特性<br>DWORD dwCreationDisposition, //文件操作<br>DWORD dwFlagsAndAttributes, // 文件属性<br>HANDLE hTemplateFile // 如果不为零，则指定一个文件句柄。新文件将从这个文件中复制扩展属性 <br>);</p>
<p>文件的操作属性：<br>如果为零，表示只允许获取与一个设备有关的信息;<br>GENERIC_READ 表示允许对设备进行读访问;<br>如果为 GENERIC_WRITE 表示允许对设备进行写访问（可组合使用）;</p>
<p>文件的共享属性：<br>零表示不共享; <br>FILE_SHARE_READ 或 FILE_SHARE_WRITE 表示允许对文件进行读/写共享访问</p>
<p>文件的操作有：<br>CREATE_NEW：创建文件;如文件存在则会出错<br>CREATE_ALWAYS：创建文件，会改写前一个文件<br>OPEN_EXISTING：文件必须已经存在。由设备提出要求<br>OPEN_ALWAYS：如文件不存在则创建它<br>TRUNCATE_EXISTING：将现有文件缩短为零长度</p>
<p>文件属性有：<br>FILE_ATTRIBUTE_ARCHIVE：标记归档属性<br>FILE_ATTRIBUTE_COMPRESSED：将文件标记为已压缩，或者标记为文件在目录中的默认压缩方式<br>FILE_ATTRIBUTE_NORMAL：默认属性<br>FILE_ATTRIBUTE_HIDDEN：隐藏文件或目录<br>FILE_ATTRIBUTE_READONLY：文件为只读<br>FILE_ATTRIBUTE_SYSTEM：文件为系统文件<br>FILE_FLAG_WRITE_THROUGH：操作系统不得推迟对文件的写操作<br>FILE_FLAG_OVERLAPPED：允许对文件进行重叠操作<br>FILE_FLAG_NO_BUFFERING：禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块<br>FILE_FLAG_RANDOM_ACCESS：针对随机访问对文件缓冲进行优化<br>FILE_FLAG_SEQUENTIAL_SCAN：针对连续访问对文件缓冲进行优化<br>FILE_FLAG_DELETE_ON_CLOSE：关闭了上一次打开的句柄后，将文件删除。特别适合临时文件<br>可以组合的属性有：FILE_FLAG_WRITE_THROUGH，FILE_FLAG_OVERLAPPED，FILE_FLAG_NO_BUFFERING，FILE_FLAG_RANDOM_ACCESS，FILE_FLAG_SEQUENTIAL_SCAN，FILE_FLAG_DELETE_ON_CLOSE，FILE_FLAG_BACKUP_SEMANTICS，FILE_FLAG_POSIX_SEMANTICS，FILE_FLAG_OPEN_REPARSE_POINT，FILE_FLAG_OPEN_NO_RECALL</p>
<p>如果成功返回一个打开文件得句柄，<br>如果调用函数之前文件存在，文件操作属性为：CREATE_ALWAYS 或 OPEN_ALWAYS，使用GetLastError函数返回的是ERROR_ALREADY_EXISTS（包括函数操作成功），<br>如果之前函数不存在，则返回0。<br>使用失败返回INVALID_HANDLE_VALUE，<br>要取得更多的信息，使用GetLastError函数。</p>
<p>文件的关闭用：<br>BOOL CloseHandle(<br>HANDLE hObject // handle to object to close<br>);</p>
<p>例子1</p>
<p>在当前目录下面创建一个文件：<br>HANDLE handle;<br>DWORD Num;<br>handle= ::CreateFile("new.tmp",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_FLAG_DELETE_ON_CLOSE,NULL);<br>if(INVALID_HANDLE_VALUE!= handle )<br>{<br>::SetFilePointer(handle,0,0,FILE_BEGIN);<br>char Buffer[] = "这是个刚创建的文件";<br>::WriteFile(handle,Buffer,sizeof(Buffer),&amp;Num,NULL);<br>ZeroMemory(Buffer,sizeof(Buffer));<br>::SetFilePointer(handle,0,0,FILE_BEGIN);<br>::ReadFile(handle,Buffer,sizeof(Buffer),&amp;Num,NULL);<br>MessageBox(Buffer);<br>::CloseHandle(handle); <br>}<br>可以改变上面的创建文件的属性和操作看下不同效果。</p>
<p>CFile创建和打开一个文件：<br>创建文件和打开文件的方法有很多种，下面简单介绍下几个构造函数：<br>CFile( LPCTSTR lpszFileName, UINT nOpenFlags );throw( CFileException );<br>CFile( );<br>BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL );<br>lpszFileName:文件名称，可以是相对路径，绝对路径或网络路径<br>nOpenFlags：打开方式有：<br>CFile::modeCreate 调用构造函数构造一个新文件，如果文件已存在，则长度变成0。<br>CFile::modeNoTruncate 此值与modeCreate组合使用。如果所创建的文件已存在则其长度不变为0。因而此文件被打开，或者作为一个新文件或者作为一个已存在的文件。这将是很有用的，例如当打开一个可能存在也可能不存在的文件时。<br>CFile::modeRead 打开文件仅供读。<br>CFile::modeReadWrite 打开文件供读写。<br>CFile::modeWrite 打开文件仅供写。<br>CFile::modeNoInherit 阻止文件被子进程继承。<br>CFile::ShareDenyNone 不禁止其它进程读或写访问，打开文件。如果文件已被其它进程以兼容模式打开，则Create失败。<br>CFile::ShareDenyRead 打开文件，禁止其它进程读此文件。如果文件已被其它进程以兼容模式打开，或被其它进程读，则Create失败<br>CFile::ShareDenyWrite 打开文件，禁止其它进程写此文件。如果文件已被其它进程以兼容模式打开，或被其它进程写，则Create失败。<br>CFile::ShareExclusive 以独占模式打开文件，禁止其它进程对文件的读写。如果文件已经以其它模式打开读写（即使被当前进程），则构造失败。<br>CFile::ShareCompat 此标志在32位MFC中无效。此标志在使用CFile:: Open时映射为CFile::ShareExclusive。<br>CFile::typeText 对回车换行设置特殊进程（仅用于派生类）。<br>CFile::typeBinary 设置二进制模式（仅用于派生类）。</p>
<p>下面给出MSDN中的一个例子：<br>char* pFileName = "test.dat";<br>TRY<br>{<br>CFile f( pFileName, CFile::modeCreate | CFile::modeWrite );<br>}<br>CATCH( CFileException, e )<br>{<br>#ifdef _DEBUG<br>afxDump &lt;&lt; "File could not be opened " &lt;&lt; e-&gt;m_cause &lt;&lt; "\n";<br>#endif<br>}<br>END_CATCH</p>
<p>CFile fileTest;<br>char* pFileName = "test.dat";<br>TRY<br>{<br>fileTest.Open(pFileName, CFile::modeCreate |CFile::modeWrite);<br>}<br>CATCH_ALL(e)<br>{<br>fileTest.Abort( );<br>THROW_LAST ( );<br>}<br>END_CATCH_ALL</p>
<p>二．文件的读写定位：</p>
<p>（一）定位文件中的数据是很重要的，这决定了写入的数据在文件中的位置。</p>
<p>1、API函数<br>DWORD SetFilePointer(<br>HANDLE hFile, //文件的句柄<br>LONG lDistanceToMove, //字节偏移量r<br>PLONG lpDistanceToMoveHigh, //指定一个长整数变量，其中包含了要使用的一个高双字偏移（一般用来操作大型文件）。可设为零，表示只使用lDistanceToMove <br>DWORD dwMoveMethod //文件定位<br>);</p>
<p>dwMoveMethod文件定位的方式有三种：<br>FILE_BEGIN：从文件开始处。<br>FILE_CURRENT：从当前位置。<br>FILE_END：从文件的末尾。<br>此函数可以用来定位大型文件，lpDistanceToMoveHigh是高32位，lDistanceToMove是低32位。如果lpDistanceToMoveHigh为NULL时，函数操作成功，返回的是当前文件数据的偏移量，如果lpDistanceToMoveHigh不NULL，则返回数据的偏移量高32位放在lpDistanceToMoveHigh中，函数调用失败返回的是0xffffffff.</p>
<p>BOOL SetEndOfFile(<br>HANDLE hFile //文件的句柄<br>);</p>
<p>2、CFile类的文件数据定位函数有：</p>
<p>LONG Seek(LONG lOff,UINT nFrom);throw(CFileException);<br>如果要求的位置合法，则Seek返回从文件开始起的新字节偏移量<br>lOff：指针移动的字节数。<br>nFrom：指针移动的模式。可以是CFile::begin，CFile::current，CFile::end</p>
<p>void SeekToBegin( );</p>
<p>DWORD SeekToEnd( );//返回文件长度（字节数）。</p>
<p>下面是一个读取位图文件的信息的例子：<br>CFile file;<br>BITMAPINFOHEADER bmpinfo;<br>try<br>{<br>file.Open("D:\\ToolBar.bmp",CFile::modeRead);<br>file.Seek(sizeof(BITMAPFILEHEADER),CFile::begin);<br>file.Read(&amp;bmpinfo,sizeof(BITMAPINFOHEADER ));<br>CString str;<br>str.Format("位图文件的长是%d,高%d",bmpinfo.biWidth,bmpinfo.biHeight);<br>MessageBox(str);<br>file.Close();<br>}<br>catch(CFileException *e)<br>{<br>CString str;<br>str.Format("读取数据失败的原因是:%d",e-&gt;m_cause);<br>MessageBox("str");<br>file.Abort();<br>e-&gt;Delete();<br>}<br>（二）读取数据：</p>
<p>1、BOOL ReadFile(<br>HANDLE hFile, //文件的句柄<br>LPVOID lpBuffer, //用于保存读入数据的一个缓冲区<br>DWORD nNumberOfBytesToRead, //要读入的字符数<br>LPDWORD lpNumberOfBytesRead, //从文件中实际读入的字符数<br>LPOVERLAPPED lpOverlapped //如文件打开时指定了FILE_FLAG_OVERLAPPED，那么必须，用这个参数引用一个特殊的结构。该结构定义了一次异步读取操作。否则，应将这个参数设为NULL<br>);</p>
<p>2、CFile的成员函数有：<br>UINT Read (void* lpBuf,UINT nCount); throw(CFileException);// 返回值是传输到缓冲区的字节数。</p>
<p>（三）写入数据：</p>
<p>1、BOOL WriteFile(<br>HANDLE hFile, //文件的句柄<br>LPCVOID lpBuffer, //要写入的一个数据缓冲区<br>DWORD nNumberOfBytesToWrite, //要写入数据的字节数量。如写入零字节，表示什么都不写入，但会更新文件的&#8220;上一次修改时间&#8221;。<br>LPDWORD lpNumberOfBytesWritten, //实际写入文件的字节数量<br>LPOVERLAPPED lpOverlapped // OVERLAPPED，倘若在指FILE_FLAG_OVERLAPPED的前提下打开文件，这个参数就必须引用一个特殊的结构。该结构定义了一次异步写操作。否则，该参数应置为NULL<br>);</p>
<p>2、void Write(const void* lpBuf,UINT nCount);throw (CFileException);<br>lpBuf：指向用户提供的缓冲区，包含将写入文件中的数据<br>nCount：从缓冲区内传输的字节数。对文本模式的文件，回车换行作为一个字符。<br>下面是象一个文件中写入数据的例子：<br>CFile file;<br>try<br>{<br>file.Open("d:/my.dat",CFile::modeCreate|CFile::modeWrite);<br>file.SeekToBegin();<br>char Data[] = "111111111\n1111111111";<br>file.Write(Data,sizeof(Data));<br>file.Flush();<br>file.Close();<br>}<br>catch(CFileException *e)<br>{<br>CString str;<br>str.Format("读取数据失败的原因是:%d",e-&gt;m_cause);<br>MessageBox("str");<br>file.Abort();<br>e-&gt;Delete();<br>}</p>
<p>三．取得和设置文件的创建时间、最后访问时间、最后写时间</p>
<p>BOOL GetFileTime(<br>HANDLE hFile, // 文件句柄<br>LPFILETIME lpCreationTime, // 创建时间<br>LPFILETIME lpLastAccessTime, // 最后访问时间<br>LPFILETIME lpLastWriteTime // 最后写时间<br>);</p>
<p>BOOL SetFileTime(<br>HANDLE hFile, <br>CONST FILETIME *lpCreationTime, <br>CONST FILETIME *lpLastAccessTime, <br>CONST FILETIME *lpLastWriteTime <br>);<br>typedef struct _FILETIME { <br>DWORD dwLowDateTime; <br>DWORD dwHighDateTime; <br>} FILETIME;<br>取得三个参数都是FILETIME结构，得到的都是UTC时间，可以通过API函数FileTimeToLocalFileTime（）和FileTimeToSystemTime()将他们转换为本地时间和系统时间格式，也可以通过LocalFileTimeToFileTime和SystemTimeToFileTime()转换回来，通过SetFileTime设置文件的创建时间、最后访问时间、最后写时间。由于使用的时候要先打开文件，而且取得的最后访问时间就是当前时间，没有多大意义，且比较麻烦，下面介绍CFile类中的静态方法。<br>static BOOL PASCAL GetStatus( LPCTSTR lpszFileName, CFileStatus&amp; rStatus );<br>static void SetStatus( LPCTSTR lpszFileName, const CFileStatus&amp; status );<br>throw( CFileException );<br>返回的是一个CfileStatus对象，这个结构的具体的成员变量包括：<br>struct CFileStatus<br>{<br>CTime m_ctime; // 文件创建时间<br>CTime m_mtime; // 文件最近一次修改时间<br>CTime m_atime; // 文件最近一次访问时间<br>LONG m_size; // 文件大小<br>BYTE m_attribute; // 文件属性<br>BYTE _m_padding; // 没有实际含义，用来增加一个字节<br>TCHAR m_szFullName[_MAX_PATH]; //绝对路径<br>#ifdef _DEBUG<br>//实现Dump虚拟函数，输出文件属性<br>void Dump(CDumpContext&amp; dc) const;<br>#endif<br>};</p>
<p>下面就举一个例子来实现：<br>CFileStatus status;<br>char *path = "D:\\VSS";<br>if(CFile::GetStatus( path, status ))<br>{<br>CString cTime,mTime,aTime;<br>cTime = status.m_ctime.Format("文件建立时间：%Y年%m月%d日 %H时%M分%S秒");<br>mTime = status.m_mtime.Format("文件最近修改时间：%Y年%m月%d日 %H时%M分%S秒");<br>aTime = status.m_atime.Format("文件最近访问时间：%Y年%m月%d日 %H时%M分%S秒");<br>CString str;<br>str = cTime + "\n" + mTime +"\n" + aTime ;<br>MessageBox(str);</p>
<p>}</p>
<p>四．取得和设置文件的属性</p>
<p>DWORD GetFileAttributes(<br>LPCTSTR lpFileName //文件或文件夹路经<br>);</p>
<p>BOOL SetFileAttributes(<br>LPCTSTR lpFileName, // 文件名<br>DWORD dwFileAttributes // 要设置的属性<br>);</p>
<p>取得的文件属性包括：FILE_ATTRIBUTE_ARCHIVE，FILE_ATTRIBUTE_HIDDEN，FILE_ATTRIBUTE_NORMAL，FILE_ATTRIBUTE_OFFLINE，FILE_ATTRIBUTE_READONLY，FILE_ATTRIBUTE_SYSTEM，FILE_ATTRIBUTE_TEMPORARY</p>
<p>不能设置的文件属性包括有：FILE_ATTRIBUTE_COMPRESSED，FILE_ATTRIBUTE_DIRECTORY，FILE_ATTRIBUTE_ENCRYPTED，FILE_ATTRIBUTE_REPARSE_POINT，FILE_ATTRIBUTE_SPARSE_FILE，FILE_ATTRIBUTE_SYSTEM。</p>
<p>CFileStatus中也定义了一组属性：<br>enum Attribute { <br>normal,<br>readOnly,<br>hidden,<br>system,<br>volume,<br>directory,<br>archive<br>};<br>可以通过if((status. m_attribute&amp; readOnly) = =FILE_ATTRIBUTE_READONLY)来判断，这里利用另外的API来实现获得文件的详细信息：</p>
<p>HANDLE FindFirstFile(<br>LPCTSTR lpFileName, //文件或文件夹路经r<br>LPWIN32_FIND_DATA lpFindFileData <br>);</p>
<p>BOOL FindNextFile(<br>HANDLE hFindFile,<br>LPWIN32_FIND_DATA lpFindFileData <br>);</p>
<p>BOOL FindClose(<br>HANDLE hFindFile );</p>
<p>取得的是一个WIN32_FIND_DATA结构;</p>
<p>typedef struct _WIN32_FIND_DATA {<br>DWORD dwFileAttributes; //文件属性<br>FILETIME ftCreationTime; // 文件创建时间<br>FILETIME ftLastAccessTime; // 文件最后一次访问时间<br>FILETIME ftLastWriteTime; // 文件最后一次修改时间<br>DWORD nFileSizeHigh; // 文件长度高32位<br>DWORD nFileSizeLow; // 文件长度低32位<br>DWORD dwReserved0; // 系统保留<br>DWORD dwReserved1; // 系统保留<br>TCHAR cFileName[ MAX_PATH ]; // 长文件名<br>TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名<br>} WIN32_FIND_DATA, *PWIN32_FIND_DATA;</p>
<p>也可以利用另外一个函数来取得文件的信息:<br>BOOL GetFileInformationByHandle(<br>HANDLE hFile, // 文件的句柄 <br>LPBY_HANDLE_FILE_INFORMATION lpFileInformation <br>);<br>函数填充的是BY_HANDLE_FILE_INFORMATION结构体:<br>typedef struct _BY_HANDLE_FILE_INFORMATION { <br>DWORD dwFileAttributes; //文件属性<br>FILETIME ftCreationTime; // 文件创建时间<br>FILETIME ftLastAccessTime; // 文件最后一次访问时间<br>FILETIME ftLastWriteTime; // 文件最后一次修改时间<br>DWORD dwVolumeSerialNumber; // 文件所在的磁盘的序列号<br>DWORD nFileSizeHigh; // 文件长度高32位<br>DWORD nFileSizeLow; // 文件长度低32位<br>DWORD nNumberOfLinks; //链接的数目<br>DWORD nFileIndexHigh; <br>DWORD nFileIndexLow; <br>} BY_HANDLE_FILE_INFORMATION;</p>
<p>下面就举一个例子来实现：<br>HANDLE handle;<br>WIN32_FIND_DATA find_data;<br>handle = :: FindFirstFile("D:\\VSS",&amp;find_data);<br>FindClose(handle);<br>find_data.dwFileAttributes = find_data.dwFileAttributes|FILE_ATTRIBUTE_READONLY;<br>::SetFileAttributes("D:\\VSS",find_data.dwFileAttributes);<br>在上面的介绍中,除了可以设置文件的属性之外，在操作的过程当中也可以取得文件的其他一些信息，可以根据具体的需要来实现。</p>
<p>五．获取文件名,文件类型,文件长度,文件路径<br>用利用CFile打开一个文件时,可以在利用成员函数<br>virtual CString GetFileName( ) const, <br>virtual CString GetFileTitle( ) const, <br>virtual CString GetFilePath( ) const, <br>virtual DWORD GetLength( ) const;throw( CFileException );<br>来取得相关信息,如果一个文件的全路经是: c:\windows\write\myfile.wri,则每个函数取得的是: myfile.wri, myfile, c:\windows\write\myfile.wri. GetLength取得文件大小是按字节为单位的.<br>也可以利用：<br>virtual void SetLength( DWORD dwNewLen );throw( CFileException );<br>virtual void SetFilePath( LPCTSTR lpszNewName );<br>来设置文件的长度和路径。<br>在当前的文件下面新建一个Text.txt文件，在里面写点东西，然后运行下面程序：<br>CFile file("Text.txt",CFile::modeReadWrite);<br>ULONGLONG length;<br>CString strFilePath;<br>length = file.GetLength();<br>length = length + 1024*10;<br>file.SetLength(length);<br>file.SetFilePath("D:\\Text.txt");<br>strFilePath = file.GetFilePath();<br>MessageBox(strFilePath);<br>file.Close();<br>最后发现文件的路径变了，但是在D盘下面并没有找到Text.txt，原因是SetFilePath只能指定一个路径给文件，SetFilePath并不能做为移动文件来使用。<br>CFile并没有给出取得文件类型的函数，有了上面基础，这个很容易实现。<br>API函数中也有获得文件路径的操作，这里只是做简单介绍，可以参照MSDNN的说明：<br>GetFileSize可以获得文件的大小，<br>GetFullPathName 函数获取文件的完整路径名，只有当该文件在当前目录下，结果才正确。<br>GetModuleFileName函数获取文件的完整路径名，这些函数有些用到文件句柄的。<br>用CFileDialog打开的文件,可以使用它的成员变量m_ofn,或者成员函数GetFileName, GetFileTitle, GetFilePath,GetFileExt来取得相关信息.<br>CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL );<br>各个参数如下：<br>bOpenFileDialog 为TRUE为打开对话框，为FALSE为保存对话文件对话框<br>lpszDefExt 指定默认的文件扩展名。 <br>lpszFileName 指定默认的文件名。 <br>dwFlags 指明一些特定风格。 <br>lpszFilter它指明可供选择的文件类型和相应的扩展名。参数格式如： <br>"Chart Files (*.xlc)|*.xlc|Worksheet Files (*.xls)|*.xls|Data Files (*.xlc;*.xls)|*.xlc; *.xls|All Files (*.*)|*.*||";文件类型说明和扩展名间用 | 分隔，同种类型文件的扩展名间可以用 ; 分割，每种文件类型间用 | 分隔，末尾用 || 指明。 <br>pParentWnd 为父窗口指针<br>CString FileFilter = "所有文件(*.*)|*.*||";<br>CFileDialog FileDialog(true,NULL,NULL,OFN_HIDEREADONLY,FileFilter,NULL);<br>FileDialog.DoModal();<br>MessageBox(FileDialog.GetFileName());<br>6．小结：<br>在实际中还有很多其他操作文件的方法，上面介绍的只是简单的几种，希望通过上面的简单介绍，在加上具体实践，能够找到解决问题的最好办法！</p>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44581.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-15 18:19 <a href="http://www.cppblog.com/Raycruiser/articles/44581.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SetViewportOrg与SetWindowOrg的理解</title><link>http://www.cppblog.com/Raycruiser/articles/44255.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Wed, 12 Mar 2008 04:39:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44255.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44255.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44255.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44255.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44255.html</trackback:ping><description><![CDATA[总的来说，我对这两个函数的理解导致的结果是与实际程序运行的结果截然相反。依据MSDN上的解释，有一个很严重的问题没有阐述清楚，那就是：所谓的SetWindowOrg(x, y)函数，到底是表示set window origin to (x, y)还是set window origin as (x, y)；to和as在执行的时候，其操作的效果是截然相反的。<br>set window origin to (x, y)表示将坐标原点设置到(x, y)；即以(x, y)作为坐标原点，此时原点坐标不再为(0, 0)；<br>set window origin as (x, y)表示将原来的原点(0, 0)的坐标改为(x, y)；即将所有点的坐标增加(+x, +y)；<br>现在我的理解是：应该是 set window origin to (x, y)。这种理解基于以下几个前提：<br>1. 所有绘图语句中给出的坐标，全部是逻辑坐标，即在 window 中的坐标(相对于viewport所表示的设备坐标而言);<br>2. 所有用户能看到的点，其设备坐标一定是位于(0, 0)和(1024, 768)范围内；(假设显示器为输出设备，采用MM_TEXT映射方式，且屏幕分辨率为1024*768);<br>3. 所谓&#8220;(0,0)就原点，原点的坐标一定就是(0,0)&#8221;这种理解，是错误的；<br>4. Viewport中的坐标表示设备坐标；Window中的坐标表示逻辑坐标；<br>5. 当在逻辑坐标中指定新的原点后，在执行映射时，设备坐标的原点一定要与逻辑坐标的新原点重合；反过来也是一样，即两个坐标系的原点一定要重合。<br>下面举例说明：(MM_TEXT映射模式)<br>(1)<br>CRect rect(0, 0, 200, 200);<br>dc.rectangle(rect);<br>上面的语句在屏幕的最左上角绘制一个正方形;(因为此时逻辑坐标与设备坐标没有偏移)<br>(2)<br>dc.SetViewportOrg(100, 100);<br>CRect rect(0, 0, 200, 200);<br>dc.rectangle(rect);<br>将设备坐标的原点设置到(100, 100)；即设备坐标的原点不在(0, 0)处，而是在(100, 100)处；此时若执行映射的话，逻辑坐标的原点(0, 0)需要与设备坐标的原点(100, 100)重合(参考前提5)；那么此时绘制的矩形(0, 0, 200, 200)的坐标(为逻辑坐标，参考前提1)在设备坐标中就会映射为(100, 100, 300, 300)，最终我们在显示器上看到的会是一个向右下方偏移(100, 100)的一个边长为200的正方形(用户看到的点是在设备坐标中的，参考前提2)<br>(3)<br>dc.SetWindowOrg(100, 100);<br>CRect rect(0, 0, 200, 200);<br>dc.rectangle(rect);<br>将逻辑坐标的原点设置到(100, 100)；即逻辑坐标的原点不在(0, 0)处，而是在(100, 100)处；此时若执行映射的话，设备坐标的原点(0, 0)需要与逻辑坐标的原点(100, 100)重合(参考前提5)；那么此时绘制的矩形(0, 0, 200, 200)的坐标(为逻辑坐标，参考前提1)在设备坐标中就会映射为(-100, -100, 100, 100)，最终我们在显示器上看到的会是一个只有1/4个大小的矩形的一部分(事实上相当于向左上方偏移(100, 100)的一个边长为200的正方形。注意：用户看到的点是在设备坐标中的，参考前提2)<br>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44255.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-12 12:39 <a href="http://www.cppblog.com/Raycruiser/articles/44255.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>按下了关闭按钮以后windows的处理步骤和消息流程</title><link>http://www.cppblog.com/Raycruiser/articles/44201.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 11 Mar 2008 08:47:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44201.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44201.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44201.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44201.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44201.html</trackback:ping><description><![CDATA[<p>1、当你按下了&#8220;关闭&#8221;按钮或选取了&#8220;退出&#8221;菜单项之后，WM_CLOSE消息被发送给了应用程序的窗口处理过程函数。该函数对该消息的缺省处理仅为调用DestroyWindow()API函数，导致窗口被销毁。<br>2、当窗口的<span style="COLOR: #ff0000">客户区被销毁</span>的时候WM_DESTROY消息被发给了应用程序的窗口处理过程。程序员可以在此<span style="COLOR: #ff0000">释放他自己分配的与窗口客户区相关的资源</span>，而窗口过程函数<span style="COLOR: #ff0000">缺省的处理仅为调用PostQuitMessage（）函数</span>，这将导致WM_QUIT消息被送进应用程序的消息队列。<br>3、随后窗口的<span style="COLOR: #ff0000">非客户区（包括菜单栏、标题栏、状态栏等）被销毁</span>，此时WM_NCDESTROY消息被发送给了应用程序的窗口过程。这个消息也是窗口过程处理的最后一个消息，程序员可以在这里释放他自己分配的而一直没有释放的内存。使用了MFC创建的应用程序往往要在这个消息响应函数当中进行一些释放内存和资源的工作。<br>4、而应用程序的消息检测函数GetMessage()一旦检索到WM＿QUIT消息，就会返回False，从而结束消息循环，并且让应用程序的WinMain()函数返回，从而结束应用程序。因此应用程序的窗口处理过程是不可能处理到这条WM＿QUIT消息的。<br>5、综上所述，如果我们是用SDK方法编写应用程序而又不需要在程序结束的时候释放用new及其他内存分配函数显式分配的内存的话，我们完全可以直接通过向应用程序发送WM_QUIT消息让程序退出。<br>6、如果我们利用了MFC创建应用程序则最好通过发送WM_CLOSE消息让应用程序结束。因为MFC往往要在最后时刻进行一些清理工作。</p>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44201.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-11 16:47 <a href="http://www.cppblog.com/Raycruiser/articles/44201.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WM_DESTROY 和 WM_NCDESTROY </title><link>http://www.cppblog.com/Raycruiser/articles/44200.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 11 Mar 2008 08:32:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44200.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44200.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44200.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44200.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44200.html</trackback:ping><description><![CDATA[在窗口销毁时有两个紧密关联的 windows 消息, 就是 WM_DESTROY 和 WM_NCDESTROY. 它们有何区别? <br><br>区别就是 WM_DESTROY 消息是在窗口销毁动作序列中的开始被发送的, 而 WM_NCDESTROY 消息是在结尾. 这在你的窗口拥有子窗口时是个重大区别. 如果你有一个带子窗口的父窗口, 那么消息的发送序列 (在没有怪诞行为影响的前提下) 就像这样: <br><br>hwnd = parent, uMsg = WM_DESTROY<br>hwnd = child, uMsg = WM_DESTROY<br>hwnd = child, uMsg = WM_NCDESTROY<br>hwnd = parent, uMsg = WM_NCDESTROY<br><br>注意, 父窗口是在子窗口被销毁之前收到 WM_DESTROY 消息, 在子窗口被销毁之后收到 WM_NCDESTROY 消息. <br><br>两个销毁消息, 一个在开头, 一个在结尾, 这意味着, 对于你自己的模块, 你可以通过处理相应的消息来执行清理操作. 例如, 如果有些东西必须在开头清理, 那么你可以使用 WM_DESTROY 消息. <br><br>WM_NCDESTROY 消息是你窗口将会收到的最后一个消息 (在没有怪诞行为影响的前提下), 因此, 这里是做 "最终清理" 的最佳场所. 这就是为什么我们的 <a href="http://blogs.msdn.com/oldnewthing/archive/2005/04/22/410773.aspx"><font color=#1a8bc8><u>new scratch</u></font></a> 程序会一直等到 WM_NCDESTROY 销毁它的实例变量, 才会返回. <br><br>与这两个销毁消息配对的, 是 WM_CREATE 和 WM_NCCREATE 这两个类似的消息. 与 WM_NCDESTROY 是你窗口收到的最后一条消息类似, WM_NCCREATE 消息是第一条消息, 这是一个创建你自己的实例变量的好地方. 需要注意的是, 如果你导致 WM_NCCREATE 消息返回失败, 那么所有你将收到的消息就只有 WM_NCDESTROY 了; 不会有 WM_DESTROY 消息了, 因为你根本就没有收到相应的 WM_CREATE 消息. <br>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44200.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-11 16:32 <a href="http://www.cppblog.com/Raycruiser/articles/44200.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WM_DESTROY， WM_CLOSE， WM_QUIT</title><link>http://www.cppblog.com/Raycruiser/articles/44199.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 11 Mar 2008 08:29:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44199.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44199.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44199.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44199.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44199.html</trackback:ping><description><![CDATA[<p>WM_DESTROY 是关闭程序的&nbsp;&nbsp;&nbsp;&nbsp; <br>WM_CLOSE&nbsp;&nbsp; 是关闭窗口的&nbsp;&nbsp; <br>WM_QUIT&nbsp;&nbsp;&nbsp; 是关闭消息环的 </p>
<p>以下是程序结束的过程：<br>1、使用者按[File/Close],系统发出WM_CLOSE消息<br>2、Frame把这条消息直接发给预处理程序<br>3、预处理程序发出WM_DESTROY消息<br>4、预处理程序收到WM_DESTROY后执行PostQuitMessage，发出WM_QUIT.<br>5、GetMessage收到这条消息后就推出消息循环，程序结束。</p>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44199.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-11 16:29 <a href="http://www.cppblog.com/Raycruiser/articles/44199.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Invalidate()</title><link>http://www.cppblog.com/Raycruiser/articles/44193.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 11 Mar 2008 07:34:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44193.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44193.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44193.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44193.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44193.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 24pt">InvalidateRect只是增加重绘区域，在下次WM_PAINT的时候才生效</p>
<p>InvalidateRect函数中的参数TRUE表示系统会在你画之前用背景色将所选区域覆盖一次，默认背景色为白色，可以通过设置BRUSH来改变背景色。</p>
<p>Invalidate()之后：<br>...OnPaint()-&gt;OnPrepareDC()-&gt;OnDraw()<br>所以只是刷新在OnPaint()和OnDraw()函数中的绘图语句。其它地方没有影响。</p>
<p>Invalidate标记一个需要重绘的无效区域，并不意味着调用该函数后就立刻进行重绘。类似于PostMessage(WM_PAINT)，需要处理到WM_PAINT消息时才真正重绘。以为您Invalidate之后还有其他的语句正在执行，程序没有机会去处理WM_PAINT消息，但当函数执行完毕后，消息处理才得以进行。</p>
<p>Invalidate只是放一个WM_PAINT消息在队列里，不做别的，所以只有当当前函数返回后，进入消息循环，取出WM_PAINT，才执行PAINT，所以不管Invalidate放哪里，都是最后的。</p>
<p>InvalidateRect(hWnd,&amp;rect,TRUE);向hWnd窗体发出WM_PAINT的消息，强制客户区域重绘制，<br>rect是你指定要刷新的区域，此区域外的客户区域不被重绘，这样防止客户区域的一个局部的改动，而导致整个客户区域重绘而导致闪烁，如果最后的参数为TRUE，则还向窗体发送WM_ERASEBKGND消息，使背景重绘，当然在客户区域重绘之前。<br>UpdateWindow只向窗体发送WM_PAINT消息，在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域，如果没有，则不发送WM_PAINT</p>
<p>如果希望立即刷新无效区域，可以在调用InvalidateRect之后调用UpdateWindow，如果客户区的任一部分无效，则UpdateWindow将导致Windows用WM_PAINT消息调用窗口过程（如果整个客户区有效，则不调用窗口过程）。<font color=#ff0000>这一WM_PAINT消息不进入消息队列，直接由WINDOWS调用窗口过程。窗口过程完成刷新以后立刻退出，WINDOWS将控制返回给程序中UpdateWindow调用之后的语句。</font><font color=#000000>(</font>windows程序设计第5版 P98)<br></p>
<p>UpdateData()顺便说下，这个函数不是刷新界面用的。<br>UpdateData();参数为FALSE时，将界面上控件绑定的变量的数据导到控件内，参数为TRUE时，导入方向则相反</p>
<p>&#160;</p>
<div class=postTitle><font color=#00ffff><font color=#ff0000>附：Invalidate 与WM_PAINT之间的关系</font> </font></div>
<p>
<table border=0>
    <tbody>
        <tr>
            <td style="FONT-SIZE: 14pt">
            <p style="FONT-SIZE: 14pt">系统会在多个不同的时机发送WM_PAINT消息：当第一次创建一个窗口时，当改变窗口的大小时，当把窗口从另一个窗口背后移出时，当最大化或最小化窗口时，等等，这些动作都是由系统管理的，应用只是被动地接收该消息，在消息处理函数中进行绘制操作；大多数的时候应用也需要能够主动引发窗口中的绘制操作，比如当窗口显示的数据改变的时候，这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中，当应用的消息队列没有其他消息时，如果窗口的Update Region不为空时，系统就会自动产生WM_PAINT消息。 </p>
            <p style="FONT-SIZE: 14pt"></p>
            系统为什么不在调用Invalidate时发送WM_PAINT消息呢？又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢？这是因为系统把在窗口中的绘制操作当作一种低优先级的操作，于是尽可能地推后做，这样有利于提高绘制的效率：在两个WM_PAINT消息之间多个Invalidate调用使之失效的区域就会被累加起来，然后在一个WM_PAINT消息中一次得到更新，不仅能避免多次重复地更新同一区域，也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效，依赖于系统在合适的时机发送WM_PAINT消息的机 制实际上是一种异步工作方式，也就是说，在无效化窗口区域和发送WM_PAINT消息之间是有延迟的；有时候这种延迟并不是我们希望的，这时我们当然可以在无效化窗口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画，但不如使用Windows GDI为我们提供的更方便和强大的函数：UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region，当其不为空时才发送WM_PAINT消息；RedrawWindow则给我们更多的控制：是否重画非客户区和背景，是否总是发送WM_PAINT消息而不管Update Region是否为空等</td>
        </tr>
    </tbody>
</table>
<br></p>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44193.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-11 15:34 <a href="http://www.cppblog.com/Raycruiser/articles/44193.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MFC消息映射机制的剖析</title><link>http://www.cppblog.com/Raycruiser/articles/44190.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 11 Mar 2008 07:08:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44190.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44190.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44190.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44190.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44190.html</trackback:ping><description><![CDATA[<div class="entry-body nerr">
<p>一，消息映射机制</p>
<p>1，消息响应函数：（例：在CDrawView类响应鼠标左键按下消息）<br>&nbsp;1）在头文件(DrawView.h)中声明消息响应函数原型。<br>//{{AFX_MSG(CDrawView)&nbsp;&nbsp; //注释宏<br>afx_msg void OnLButtonDown(UINT nFlags, CPoint point);<br>//}}AFX_MSG&nbsp;&nbsp; //注释宏<br>说明：<br>在注释宏之间的声明在VC中灰色显示。afx_msg宏表示声明的是一个消息响应函数。<br>&nbsp;2）在源文件（DrawView.cpp)中进行消息映射。<br>BEGIN_MESSAGE_MAP(CDrawView, CView)<br>&nbsp;//{{AFX_MSG_MAP(CDrawView)<br>&nbsp;ON_WM_LBUTTONDOWN()<br>&nbsp;//}}AFX_MSG_MAP<br>&nbsp;// Standard printing commands<br>&nbsp;ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)<br>&nbsp;ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)<br>&nbsp;ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)<br>END_MESSAGE_MAP()<br>说明：<br>在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间进行消息映射。<br>宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDown（）相关联。这样一旦有消息的产生，就会自动调用相关联的消息响应函数去处理。<br>宏ON_WM_LBUTTONDOWN()定义如下：<br>#define ON_WM_LBUTTONDOWN() \<br>&nbsp;{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \<br>&nbsp;&nbsp;(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&amp;OnLButtonDown },<br>&nbsp;3）源文件中进行消息响应函数处理。（DrawView.cpp中自动生成OnLButtonDown函数轮廓，如下）<br>void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) <br>{<br>&nbsp;// TODO: Add your message handler code here and/or call default<br>&nbsp;CView::OnLButtonDown(nFlags, point);<br>}<br>说明：<br>可见当增加一个消息响应处理，在以上三处进行了修改。可在消息响应函数里添加消息处理代码完成对消息的响应、处理。</p>
<p>2，消息响应的方式：<br>1）在基类中针对每种消息做一个虚函数，当子类对消息响应时候，只要在子类中重写这个虚函数即可。缺点：MFC类派生层次很多，如果在基类对每个消息进行虚函数处理，那么从基类派生的每个子类都将背负一个庞大的虚表，这样浪费内存，故MFC没有采取这中方式而采取消息映射方式。<br>2）消息映射方式：MFC在后台维护了一个句柄和C++对象指针对照表，当收到一个消息后，通过消息结构里资源句柄（查对照表）就可找到与它对应的一个C++对象指针，然后把这个指针传给基类，基类利用这个指针调用WindowProc()函数对消息进行处理，WindowProc()函数中调用OnWndMsg()函数，真正的消息路由及处理是由OnWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函数，而且是用派生类对象指针调用的，由多态性知最总终调用子类的。在OnWndMsg()函数处理的时候，根据消息种类去查找消息映射，判断所发的消息有没有响应函数，具体方式是到相关的头文件和源文件中寻找消息响应函数声明（从注释宏//{{AFX_MSG(CDrawView)...//}}AFX_MSG之间寻找），消息映射（从宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之间寻找），最终找到对应的消息处理函数。当然，如果子类中没有对消息进行处理，则消息交由基类处理。<br>说明：<br>virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);<br>virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);</p>
<p>&#160;</p>
<p>二，有关绘图</p>
<p>1，使用SDK获取DC句柄：<br>HDC hdc;<br>hdc=::GetDc(m_hWnd);//获取DC句柄<br>MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);<br>LineTo(hdc,point.x,point.y);<br>::ReleaseDC(m_hWnd,hdc);//释放DC</p>
<p>2，利用CDC类指针和CWin类成员函数获取DC。<br>CDC *pDC=GetDC();<br>pDC-&gt;MoveTo(m_ptOrigin);<br>pDC-&gt;LineTo(point);<br>ReleaseDC(pDC);</p>
<p>3，利用CClientDC对象。（CClientDC类从CDC类派生来的）<br>CClientDC dc(this);<br>dc.MoveTo(m_ptOrigin);<br>dc.LineTo(point);<br>说明：<br>The CClientDC class is derived from CDC and takes care of calling the Windows functions GetDC at construction time and ReleaseDC at destruction time. This means that the device context associated with a CClientDC object is the client area of a window.</p>
<p>4，利用CWindowDC对象。（CWindowDC类从CDC类派生来的）<br>CWindowDC dc(this);//<br>dc.MoveTo(m_ptOrigin);<br>dc.LineTo(point);<br>说明：<br>The CWindowDC class is derived from CDC. It calls the Windows functionsGetWindowDC at construction time andReleaseDC at destruction time. This means that a CWindowDC object accesses the entire screen area of a CWnd (both client and nonclient areas).</p>
<p>5，GetParent()得到父窗口指针；GetDesktopWindow()得到屏幕窗口指针。</p>
<p>6，利用画笔改变线条颜色和类型：<br>CPen pen(PS_DOT,1,RGB(0,255,0));//构造画笔对象<br>CClientDC dc(this);CPen *pOldPen=dc.SelectObject(&amp;pen);//将画笔选入DC<br>dc.MoveTo(m_ptOrigin);<br>dc.LineTo(point);<br>dc.SelectObject(pOldPen);//恢复先前的画笔</p>
<p>7，使用画刷（通常利用画刷去填充矩形区域）：<br>使用单色画刷<br>CBrush brush(RGB(255,0,0));//构造画刷对象<br>CClientDC dc(this);<br>dc.FillRect(CRect(m_ptOrigin,point),&amp;brush);//用指定的画刷去填充矩形区域</p>
<p>使用位图画刷<br>CBitmap bitmap;//构造位图对象（使用前需要初试化）<br>bitmap.LoadBitmap(IDB_BITMAP1);//初试化位图对象<br>CBrush brush(&amp;bitmap);//构造位图画刷<br>CClientDC dc(this);<br>dc.FillRect(CRect(m_ptOrigin,point),&amp;brush);//用指定的位图画刷去填充矩形区域</p>
<p>使用透明画刷<br>CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//获取透明画刷对象指针<br>CClientDC dc(this);<br>CBrush *pOldBrush=dc.SelectObject(pBrush);//将透明画刷选入DC<br>dc.Rectangle(CRect(m_ptOrigin,point));<br>dc.SelectObject(pOldBrush);//释放透明画刷<br>说明：<br>The GetStockObject function retrieves a handle to one of the predefined stock pens, brushes, fonts, or palettes. <br>HGDIOBJ GetStockObject(<br>&nbsp; int fnObject&nbsp;&nbsp; // type of stock object<br>);</p>
<p>Returns a pointer to a CBrush object when given a handle to a Windows HBRUSH object. <br>static CBrush* PASCAL FromHandle( HBRUSH hBrush );//FromHandle是一个静态方法，故可用CBrush::FromHandle()形式调用。<br>注意点：<br>1）静态方法不属于某一个具体对象，而属于类本身，在类加载的时候就已经为类静态方法分配了代码去，故可用CBrush::FromHandle()形式调用。<br>2）静态方法中，不能引用非静态的数据成员和方法。<br>3）静态数据成员需要在类外单独做初始化，形式如： 变量类型 类名::变量名=初始值;</p>
<p>8，CDC::SetROP2方法：<br>int SetROP2( int nDrawMode );<br>Sets the current drawing mode. </p>
</div>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44190.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-11 15:08 <a href="http://www.cppblog.com/Raycruiser/articles/44190.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MFC程序框架的剖析 </title><link>http://www.cppblog.com/Raycruiser/articles/44189.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 11 Mar 2008 07:06:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44189.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44189.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44189.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44189.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44189.html</trackback:ping><description><![CDATA[<p>1,寻找WinMain人口：<br>在安装目录下找到MFC文件夹下的SRC文件夹，SRC下是MFC源代码。<br>路径：MFC|SRC|APPMODUL.CPP：<br>_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,<br>&nbsp;LPTSTR lpCmdLine, int nCmdShow)<br>{<br>&nbsp;// call shared/exported WinMain<br>&nbsp;return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);<br>}<br>注意：（#define _tWinMain&nbsp;&nbsp; WinMain）</p>
<p>2，对于全局对象或全局变量来说，在程序运行即WINMAIN函数加载的时候，已经为全局对象或全局变量分配了内存和赋初值。<br>所以：CTEApp theApp;-&gt;CTEApp ::CTEApp(){}-&gt;_tWinMain(){}<br>说明：每一个MFC程序，有且只有一个从WinApp类派生的类（应用程序类），也只有一个从应用程序类所事例化的对象，表示应用程序本身。在WIN32程序当中，表示应用程序是通过WINMAIN入口函数来表示的（通过一个应用程序的一个事例号这一个标识来表示的）。在基于MFC应用程序中，是通过产生一个应用程序对象，用它来唯一的表示了应用程序。</p>
<p>3，通过构造应用程序对象过程中调用基类CWinApp的构造函数，在CWinApp的构造函数中对程序包括运行时一些初始化工作完成了。<br>CWinApp构造函数：MFC|SRC|APPCORE.CPP<br>CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参数，而CTEApp构造函数没有显式向父类传参，难道CWinApp()有默认参数？见下：<br>（在CWinApp类定义中，&nbsp;CWinApp(LPCTSTR lpszAppName = NULL); ）<br>注意：CWinApp()函数中：<br>pThreadState-&gt;m_pCurrentWinThread = this;<br>pModuleState-&gt;m_pCurrentWinApp = this<br>（this指向的是派生类CTEApp对象，即theApp）<br>调试：CWinApp::CWinApp();-&gt;CTEApp theApp;（-&gt;CTEApp ::CTEApp()）-&gt;CWinApp::CWinApp()-&gt;CTEApp ::CTEApp()-&gt;_tWinMain(){}</p>
<p>4,_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。（Afx*前缀代表这是应用程序框架函数，是一些全局函数，应用程序框架是一套辅助生成应用程序的框架模型，把一些类做一些有机的集成，我们可根据这些类函数来设计自己的应用程序）。<br>AfxWinMain()函数路径：MFC|SRC|WINMAIN.CPP：<br>在AfxWinMain()函数中：<br>CWinApp* pApp = AfxGetApp();<br>说明：pApp存储的是指向WinApp派生类对象（theApp）的指针。<br>//_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()<br>//&nbsp;{ return afxCurrentWinApp; }</p>
<p>调用pThread-&gt;InitInstance()<br>说明：pThread也指向theApp,由于基类中virtual BOOL InitApplication()定义为虚函数，所以调用pThread-&gt;InitInstance()时候，调用的是派生类CTEApp的InitInstance()函数。</p>
<p>nReturnCode = pThread-&gt;Run();<br>说明：pThread-&gt;Run()完成了消息循环。</p>
<p>5,注册窗口类：AfxEndDeferRegisterClass();<br>AfxEndDeferRegisterClass()函数所在文件：MFC|SRC|APPCORE.CPP<br>BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...}<br>说明：设计窗口类：在MFC中事先设计好了几种缺省的窗口类，根据不同的应用程序的选择，调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。<br>调试：CWinApp::CWinApp();-&gt;CTEApp theApp;（-&gt;CTEApp ::CTEApp()）-&gt;CWinApp::CWinApp()-&gt;CTEApp ::CTEApp()-&gt;_tWinMain(){}//进入程序<br>-&gt;AfxWinMain();-&gt;pApp-&gt;InitApplication()；-&gt;pThread-&gt;InitInstance()//父类InitInstance虚函数;-&gt;CTEApp::InitInstance()//子类实现函数;-&gt;AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口类（出于文档管理，注册提前，正常的应在PreCreateWindow中进行注册）//之后进入创建窗口阶段（以下再不做调试）</p>
<p>6，PreCreateWindow()：//主要是注册窗口类<br>BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&amp; cs)<br>{<br>&nbsp;if( !CFrameWnd::PreCreateWindow(cs) )<br>&nbsp;&nbsp;return FALSE;<br>&nbsp;return TRUE;<br>}<br>说明：<br>CFrameWnd::PreCreateWindow()函数所在文件：MFC|SRC|WINFRM.CPP<br>BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT&amp; cs)<br>{<br>&nbsp;if (cs.lpszClass == NULL)<br>&nbsp;{<br>&nbsp;&nbsp;VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));<br>&nbsp;&nbsp;&nbsp;//判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册，如果没有注册则注册<br>&nbsp;&nbsp;cs.lpszClass = _afxWndFrameOrView;&nbsp; // COLOR_WINDOW background<br>&nbsp;&nbsp;&nbsp;//把注册后的窗口类名赋给cs.lpszClass<br>&nbsp;}</p>
<p>&nbsp;if ((cs.style &amp; FWS_ADDTOTITLE) &amp;&amp; afxData.bWin4)<br>&nbsp;&nbsp;cs.style |= FWS_PREFIXTITLE;</p>
<p>&nbsp;if (afxData.bWin4)<br>&nbsp;&nbsp;cs.dwExStyle |= WS_EX_CLIENTEDGE;</p>
<p>&nbsp;return TRUE;<br>}</p>
<p>其中：<br>virtual BOOL PreCreateWindow(CREATESTRUCT&amp; cs);//PreCreateWindow()是个虚函数,如果子类有则调用子类的。<br>#define VERIFY(f)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(f)<br>#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)<br>define AFX_WNDFRAMEORVIEW_REG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00008<br>const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件中，定义为全局数组。<br>//#define AFX_WNDFRAMEORVIEW&nbsp; AFX_WNDCLASS("FrameOrView")</p>
<p>7，创建窗口：<br>Create()函数路径：MFC|SRC|WINFRM.CPP:<br>CFrameWnd::Create(...){<br>&nbsp;...<br>&nbsp;CreateEx(...);//从父类继承来的，调用CWnd::CreateEx().<br>&nbsp;...<br>}</p>
<p>CWnd::CreateEx()函数路径：MFC|SRC|WINCORE.CPP<br>BOOL CWnd::CreateEx(...){<br>&nbsp;...<br>&nbsp;if (!PreCreateWindow(cs))//虚函数，如果子类有调用子类的。<br>&nbsp;{<br>&nbsp;&nbsp;PostNcDestroy();<br>&nbsp;&nbsp;return FALSE;<br>&nbsp;}<br>&nbsp;...<br>&nbsp;HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,<br>&nbsp;&nbsp;cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,<br>&nbsp;&nbsp;cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);</p>
<p>&nbsp;...<br>}<br>说明：CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系，使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数，如果子类有调用子类的。<br>HWND CreateWindowEx(<br>&nbsp; DWORD dwExStyle,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; LPCTSTR lpClassName,&nbsp; <br>&nbsp; LPCTSTR lpWindowName, <br>&nbsp; DWORD dwStyle,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; int x,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; int y,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; int nWidth,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; int nHeight,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; HWND hWndParent,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; HMENU hMenu,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; HINSTANCE hInstance,&nbsp; <br>&nbsp; LPVOID lpParam&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>);<br>typedef struct tagCREATESTRUCT { // cs <br>&nbsp;&nbsp;&nbsp; LPVOID&nbsp;&nbsp;&nbsp; lpCreateParams; <br>&nbsp;&nbsp;&nbsp; HINSTANCE hInstance; <br>&nbsp;&nbsp;&nbsp; HMENU&nbsp;&nbsp;&nbsp;&nbsp; hMenu; <br>&nbsp;&nbsp;&nbsp; HWND&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hwndParent; <br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cy; <br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cx; <br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y; <br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x; <br>&nbsp;&nbsp;&nbsp; LONG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; style; <br>&nbsp;&nbsp;&nbsp; LPCTSTR&nbsp;&nbsp; lpszName; <br>&nbsp;&nbsp;&nbsp; LPCTSTR&nbsp;&nbsp; lpszClass; <br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp; dwExStyle; <br>} CREATESTRUCT; </p>
<p>8，显示和更新窗口：<br>CTEApp类，TEApp.cpp中<br>m_pMainWnd-&gt;ShowWindow(SW_SHOW);//显示窗口，m_pMainWnd指向框架窗口<br>m_pMainWnd-&gt;UpdateWindow();//更新窗口<br>说明：<br>class CTEApp : public CWinApp{...}<br>class CWinApp : public CWinThread{...}<br>class CWinThread : public CCmdTarget<br>{<br>&nbsp;...<br>public:<br>&nbsp;CWnd* m_pMainWnd;<br>&nbsp;...<br>...<br>}</p>
<p>9，消息循环：<br>int AFXAPI AfxWinMain()<br>{&nbsp;...<br>&nbsp;// Perform specific initializations<br>&nbsp;if (!pThread-&gt;InitInstance()){...}<br>&nbsp;//完成窗口初始化工作，完成窗口的注册，完成窗口的创建，显示和更新。<br>&nbsp;nReturnCode = pThread-&gt;Run();<br>&nbsp;//继承基类Run()方法，调用CWinThread::Run()来完成消息循环<br>&nbsp;...<br>}<br>////////////////////////////////////////////////////////////////<br>CWinThread::Run()方法路径：MFC|SRC|THRDCORE.CPP<br>int CWinThread::Run()<br>{&nbsp;...<br>&nbsp;&nbsp;// phase2: pump messages while available<br>&nbsp;&nbsp;do//消息循环<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;// pump message, but quit on WM_QUIT<br>&nbsp;&nbsp;&nbsp;if (!PumpMessage())//取消息并处理<br>&nbsp;&nbsp;&nbsp;&nbsp;return ExitInstance();<br>&nbsp;&nbsp;&nbsp;...<br>&nbsp;&nbsp;} while (::PeekMessage(&amp;m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));<br>&nbsp;...<br>}<br>说明：<br>BOOL PeekMessage(,,,,)函数说明<br>The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.<br>If a message is available, the return value is nonzero.<br>If no messages are available, the return value is zero. </p>
<p>/////////////////////////////////////////////////////////////<br>BOOL CWinThread::PumpMessage()<br>{<br>&nbsp;...<br>&nbsp;if (!::GetMessage(&amp;m_msgCur, NULL, NULL, NULL))//取消息<br>&nbsp;{...}<br>&nbsp;...<br>&nbsp;// process this message<br>&nbsp;if (m_msgCur.message != WM_KICKIDLE &amp;&amp; !PreTranslateMessage(&amp;m_msgCur))<br>&nbsp;{<br>&nbsp;&nbsp;::TranslateMessage(&amp;m_msgCur);//进行消息（如键盘消息）转换<br>&nbsp;&nbsp;::DispatchMessage(&amp;m_msgCur);//分派消息到窗口的回调函数处理（实际上分派的消息经过消息映射，交由消息响应函数进行处理。）<br>&nbsp;}<br>&nbsp;return TRUE;<br>}</p>
<p>9，文档与视结构：<br>可以认为View类窗口是CMainFram类窗口的子窗口。<br>DOCument类是文档类。<br>DOC-VIEW结构将数据本身与它的显示分离开。<br>文档类：数据的存储，加载<br>视类：数据的显示，修改</p>
<p>10，文档类，视类，框架类的有机结合：<br>在CTEApp类CTEApp::InitInstance()函数中通过文档模板将文档类，视类，框架类的有机组织一起。<br>...<br>CSingleDocTemplate* pDocTemplate;<br>pDocTemplate = new CSingleDocTemplate(<br>&nbsp;IDR_MAINFRAME,<br>&nbsp;RUNTIME_CLASS(CTEDoc),<br>&nbsp;RUNTIME_CLASS(CMainFrame),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // main SDI frame window<br>&nbsp;RUNTIME_CLASS(CTEView));<br>AddDocTemplate(pDocTemplate);//增加到模板</p>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44189.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-11 15:06 <a href="http://www.cppblog.com/Raycruiser/articles/44189.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows程序内部运行机制</title><link>http://www.cppblog.com/Raycruiser/articles/44188.html</link><dc:creator>弱水一瓢</dc:creator><author>弱水一瓢</author><pubDate>Tue, 11 Mar 2008 07:02:00 GMT</pubDate><guid>http://www.cppblog.com/Raycruiser/articles/44188.html</guid><wfw:comment>http://www.cppblog.com/Raycruiser/comments/44188.html</wfw:comment><comments>http://www.cppblog.com/Raycruiser/articles/44188.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Raycruiser/comments/commentRss/44188.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Raycruiser/services/trackbacks/44188.html</trackback:ping><description><![CDATA[<p>1,windows程序设计是种事件驱动方式的程序设计，主要基于消息的。当用户需要完成某种功能时，需要调用OS某种支持，然后OS将用户的需要包装成消息，并投入到消息队列中，最后应用程序从消息队列中取走消息并进行响应。<br>2，消息结构：<br>typedef struct tagMSG {&nbsp;&nbsp;&nbsp;&nbsp; // msg <br>&nbsp;&nbsp;&nbsp; HWND&nbsp;&nbsp; hwnd;&nbsp;&nbsp;&nbsp;&nbsp; //接收消息的窗口句柄。和哪个窗口相关联。<br>&nbsp;&nbsp;&nbsp; UINT&nbsp;&nbsp; message;&nbsp; //消息标识。消息本身是什么。<br>&nbsp;&nbsp;&nbsp; WPARAM wParam;&nbsp;&nbsp; //消息的附加信息。具体取决于消息本身。&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; LPARAM lParam; <br>&nbsp;&nbsp;&nbsp; DWORD&nbsp; time;&nbsp;&nbsp;&nbsp;&nbsp; //消息投递时间。&nbsp;<br>&nbsp;&nbsp;&nbsp; POINT&nbsp; pt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //消息投递时，光标在屏幕上的位置。&nbsp;<br>} MSG; </p>
<p>3，消息队列：<br>每个应用程序OS都为它建立一个消息队列，消息队列是个先进先出的缓冲区，其中每个元素都是一个消息，OS将生成的每个消息按先后顺序放进消息队列中，应用程序总是取走当前消息队列中的第一条消息，应用程序取走消息后便知道用户的操作和程序的状态，然后对其处理即消息响应，消息响应通过编码实现。</p>
<p>4，使用VC编程除了良好的C基础外还需要掌握两方面：<br>一，消息本身。不同消息所代表的用户操作和应用程序的状态。<br>二，对于某个特定的消息来说，要让OS执行某个特定的功能去响应消息。</p>
<p>5，Window程序入口：<br>int WINAPI WinMain(<br>&nbsp; HINSTANCE hInstance,&nbsp; // 当前事例句柄。<br>&nbsp; HINSTANCE hPrevInstance,&nbsp; // 先前事例句柄。<br>&nbsp; LPSTR lpCmdLine,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 命令行指针<br>&nbsp; int nCmdShow&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // （窗口）显示的状态<br>);<br>说明：WinMain函数是Windows程序入口点函数，由OS调用，当OS启动应用程序的时候，winmain函数的参数由OS传递的。</p>
<p>6，创建一个完整的窗口需要经过下面四个操作步骤：<br>一，设计一个窗口类；如：WNDCLASS wndcls;<br>二，注册窗口类；&nbsp;&nbsp;&nbsp; 如：RegisterClass(&amp;wndcls);<br>三，创建窗口；&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如：CreateWindow(),CreateWindowEX();<br>四，显示及更新窗口。如：ShowWindow(),UpdateWindow();</p>
<p>说明：创建窗口的时候一定要基于已经注册的窗口类.</p>
<p>7，Windows提供的窗口类：<br>typedef struct _WNDCLASS { <br>&nbsp;&nbsp;&nbsp; UINT&nbsp;&nbsp;&nbsp; style;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //窗口的类型<br>&nbsp;&nbsp;&nbsp; WNDPROC lpfnWndProc;&nbsp; //窗口过程函数指针（回调函数）<br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp; cbClsExtra; //窗口类附加字节，为该类窗口所共享。通常0。<br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp; cbWndExtra; //窗口附加字节。通常设为0。<br>&nbsp;&nbsp;&nbsp; HANDLE&nbsp; hInstance;&nbsp; //当前应用程序事例句柄。<br>&nbsp;&nbsp;&nbsp; HICON&nbsp;&nbsp; hIcon;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //图标句柄 LoadIcon();<br>&nbsp;&nbsp;&nbsp; HCURSOR hCursor;&nbsp;&nbsp;&nbsp; //光标句柄 LoadCursor();<br>&nbsp;&nbsp;&nbsp; HBRUSH&nbsp; hbrBackground; //画刷句柄 (HBRUSH)GetStockObject();<br>&nbsp;&nbsp;&nbsp; LPCTSTR lpszMenuName;&nbsp; //菜单名字<br>&nbsp;&nbsp;&nbsp; LPCTSTR lpszClassName; //类的名字 <br>} WNDCLASS; </p>
<p>8,窗口类注册：<br>ATOM RegisterClass(<br>&nbsp; CONST WNDCLASS *lpWndClass&nbsp;&nbsp; // address of structure with class <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // data<br>);</p>
<p>9，创建窗口：<br>HWND CreateWindow(<br>&nbsp; LPCTSTR lpClassName,&nbsp; // pointer to registered class name<br>&nbsp; LPCTSTR lpWindowName, // pointer to window name<br>&nbsp; DWORD dwStyle,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // window style<br>&nbsp; int x,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // horizontal position of window<br>&nbsp; int y,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // vertical position of window<br>&nbsp; int nWidth,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // window width<br>&nbsp; int nHeight,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // window height<br>&nbsp; HWND hWndParent,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // handle to parent or owner window<br>&nbsp; HMENU hMenu,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // handle to menu or child-window identifier<br>&nbsp; HANDLE hInstance,&nbsp;&nbsp;&nbsp;&nbsp; // handle to application instance<br>&nbsp; LPVOID lpParam&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // pointer to window-creation data<br>);</p>
<p>10,显示和更新窗口窗口：<br>BOOL ShowWindow(<br>&nbsp; HWND hWnd,&nbsp;&nbsp;&nbsp;&nbsp; // handle to window<br>&nbsp; int nCmdShow&nbsp;&nbsp; // show state of window<br>);<br>BOOL UpdateWindow(<br>&nbsp; HWND hWnd&nbsp;&nbsp; // handle of window<br>);</p>
<p>11,消息循环：<br>MSG msg;<br>while(GetMessage(&amp;msg,...))&nbsp;&nbsp;&nbsp; //从消息队列中取出一条消息<br>{<br>&nbsp;TranslateMessage(&amp;msg); //进行消息（如键盘消息）转换<br>&nbsp;DispatchMessage(&amp;msg);&nbsp;//分派消息到窗口的回调函数处理,（OS调用窗口回调函数进行处理）。<br>}</p>
<p>其中：<br>//**The GetMessage function retrieves a message from the calling thread's message queue and places it in the specified structure. <br>//**If the function retrieves a message other than WM_QUIT, the return value is nonzero.If the function retrieves the WM_QUIT message, the return value is zero. If there is an error, the return value is -1. </p>
<p>BOOL GetMessage(<br>&nbsp; LPMSG lpMsg,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // address of structure with message<br>&nbsp; HWND hWnd,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // handle of window<br>&nbsp; UINT wMsgFilterMin,&nbsp; // first message<br>&nbsp; UINT wMsgFilterMax&nbsp;&nbsp; // last message<br>);</p>
<p><br>//The TranslateMessage function translates virtual-key messages into character messages. The character messages are posted to the calling thread's message queue, to be read the next time the thread calls the GetMessage or PeekMessage function. <br>BOOL TranslateMessage(<br>&nbsp; CONST MSG *lpMsg&nbsp;&nbsp; // address of structure with message<br>);</p>
<p>//The DispatchMessage function dispatches a message to a window procedure. <br>LONG DispatchMessage(<br>&nbsp; CONST MSG *lpmsg&nbsp;&nbsp; // pointer to structure with message<br>);</p>
<p><br>12，窗口过程函数（回调函数）原型：<br>The WindowProc function is an application-defined function that processes messages sent to a window. The WNDPROC type defines a pointer to this callback function. WindowProc is a placeholder（占位符） for the application-defined function name. </p>
<p>LRESULT CALLBACK WindowProc(&nbsp; //这里WindowProc是个代号名字。<br>&nbsp; HWND hwnd,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // handle to window<br>&nbsp; UINT uMsg,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // message identifier<br>&nbsp; WPARAM wParam,&nbsp; // first message parameter<br>&nbsp; LPARAM lParam&nbsp;&nbsp; // second message parameter<br>);</p>
<p>说明：两种函数调用约定（__stdcall 和 __cdecl）：<br>#define CALLBACK&nbsp;&nbsp;&nbsp; __stdcall <br>//__stdcall 标准调用预定，是PASCAL 调用约定，象DELPHI使用的就是标准调用约定<br>#define WINAPIV&nbsp;&nbsp;&nbsp;&nbsp; __cdecl&nbsp; <br>// __cdecl 是C 语言形式的调用约定。</p>
<p><br>主要区别：函数参数传递顺序 和 对堆栈的清除上。<br>问题：除了那些可变参数的函数调用外，其余的一般都是__stdcall约定。但 C/C++编译默然的是__cdecl约定。所以如果在VC等环境中调用__stdcall约定的函数，必须要在函数声明的时加上 __stdcall 修饰符，以便对这个函数的调用是使用__stdcall约定（如使用DELPHI编写的DLL时候）。<br>（VC中可通过这途径修改：project|settings..|c/c++|...)</p>
<p><br>在窗口过程函数中通过一组switch语句来对消息进行处理：<br>如：<br>LRESULT CALLBACK WindowProc(&nbsp; <br>&nbsp; HWND hwnd,<br>&nbsp; UINT uMsg,<br>&nbsp; WPARAM wParam,<br>&nbsp; LPARAM lParam&nbsp;&nbsp; <br>)<br>{<br>&nbsp;&nbsp;&nbsp; switch(uMsg)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;case WM_PAINT:<br>&nbsp;&nbsp;...<br>&nbsp;&nbsp;break;<br>&nbsp;case ...<br>&nbsp;&nbsp;break;<br>&nbsp;case WM_CLOSE:<br>&nbsp;&nbsp;//DestroyWindow(hwnd);<br>&nbsp;&nbsp;&nbsp;//销毁窗口，并发送WM_DESTROY消息。<br>&nbsp;&nbsp;break;<br>&nbsp;case WM_DESTROY:<br>&nbsp;&nbsp;//PostQuitMessage(0);<br>&nbsp;&nbsp;//发送WM_QUIT消息到消息队列中，请求终止。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //GetMessage()取到WM_QUIT消息后，返回0，退出消息循&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 环，从而终止应用程序。<br>&nbsp;&nbsp;break;<br>&nbsp;default:<br>&nbsp;&nbsp;return DefWindowProc(hwnd,uMsg,wParam,lParam);<br>&nbsp;//用缺省的窗口过程处理我们不感兴趣的消息（其它消息）。<br>&nbsp;//这是必须的。<br>&nbsp;&nbsp;&nbsp; }//switch<br>&nbsp;return 0;<br>}//WindowProc</p>
<p>13，DestroyWindow()函数和PostQuitMessage()函数原型：<br>//**The DestroyWindow function destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages。</p>
<p>BOOL DestroyWindow(<br>&nbsp; HWND hWnd&nbsp;&nbsp; // handle to window to destroy<br>);</p>
<p>//**The PostQuitMessage function indicates to the system that a thread has made a request to terminate (quit). It is typically used in response to a WM_DESTROY message. <br>//**The PostQuitMessage function posts a WM_QUIT message to the thread's message queue and returns immediately; the function simply indicates（预示，通知） to the system that the thread is requesting to quit at some time in the future. </p>
<p>When the thread retrieves the WM_QUIT message from its message queue, it should exit its message loop and return control to the system.</p>
<p>VOID PostQuitMessage(<br>&nbsp; int nExitCode&nbsp;&nbsp; // exit code<br>);</p>
<p>14，关于DC句柄获取：<br>a)使用BeginPaint(),EndPaint()对。注意只能在响应WM_PAINT消息时使用。<br>b)使用GetDc(),ReleaseDC()对。注意他们不能在响应WM_PAINT中使用。</p>
<img src ="http://www.cppblog.com/Raycruiser/aggbug/44188.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Raycruiser/" target="_blank">弱水一瓢</a> 2008-03-11 15:02 <a href="http://www.cppblog.com/Raycruiser/articles/44188.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>