<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="css/rss.xslt"?>
<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>ZwqXin - VC/MFC</title><link>http://www.zwqxin.com/</link><description>    一起学习OPENGL吧 - </description><generator>RainbowSoft Studio Z-Blog 1.8 Arwen Build 90619</generator><language>zh-CN</language><copyright>Copyright 2008-2010 ZwqXin. Some Rights Reserved. Theme edited from ipati. </copyright><pubDate>Fri, 10 Sep 2010 12:53:25 +0800</pubDate><item><title>标准MFC在OpenGL</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/MFC/formal-mfc-in-opengl.html</link><pubDate>Sun, 31 May 2009 17:46:47 +0800</pubDate><guid>http://www.zwqxin.com/archives/MFC/formal-mfc-in-opengl.html</guid><description><![CDATA[<p>怎么在一个由MFC AppWizard(EXE)建立的标准MFC框架里&ldquo;放置&rdquo;一个OpenGL程序呢？&nbsp;&nbsp;&nbsp;&nbsp;&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>说到MFC，就有文档类视图类那些关键字眼。本人虽然喜欢WIN32多于MFC，但是也无法不承认MFC能带给程序员的便利。一直写OpenGL代码所用的NEHE VC6框架其实也是基于MFC的（但是很简洁，没有虾米文档类视图类那些东西），它其实是利用了MFC的一些交互性而已，实质还是构造了一个渲染循环。具体框架的自剖可看：</p><p>[<a target="_blank" href="http://www.zwqxin.com/archives/MFC/nehe-frame-vc6.html">自剖一下自己用的NEHE OpenGL框架(上篇)</a>]</p><p>&nbsp;[<a target="_blank" href="http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html">自剖一下自己用的NEHE OpenGL框架(中篇)</a>]</p><p>[<a target="_blank" href="http://www.zwqxin.com/archives/MFC/3-nehe-frame-vc6.html.html">自剖一下自己用的NEHE OpenGL框架(下篇)</a>]</p><p>但是有时候，你不得不在标准MFC下写OPENGL代码。而这时候应该注意些什么呢？在一般写三维程序的时候，都会很留意它的实时性，因此会在程序的MAIN类函数中构建一个死循环，让程序不断重复运行渲染部分的代码，也因此有了FPS之类的概念。这样的循环也是WIN32的骨架[<a target="_blank" href="http://www.zwqxin.com/archives/MFC/windows-game-programer-2.html">WINDOWS游戏编程大师技巧笔记之&mdash;&mdash;WIN32编程</a>] ，那么，封装了WIN32的MFC应该也是这样吧？也许我对MFC真的不熟悉，但按我所理解的，MFC所封装的死循环不会轻易漏出真面目给我们放东西进去，代之的是MFC提供了VIEW类这么一种东西，让我们把绘图逻辑都放进这种基于消息的结构中。恩，在消息处理部分完成绘图。这符合绝大部分应用类程序的需求：不做无用功。</p><p>消息就是这么一种东西，它等到一定事件发生才被激活，譬如鼠标点击、移动&hellip;&hellip;绘图消息WM_PAINT也是，只有当系统或者我们告诉它窗口需要重绘时，它才动手。如果我们把我们的OPENGL绘图逻辑直接搬到这里，结果将是：只有你做一些迫使MFC窗口重绘的事，譬如最小化后又复原，譬如鼠标乱点乱移，譬如更改窗口位置或大小&hellip;&hellip;它才会&ldquo;继续渲染下去&rdquo;，否则将是静止在那儿，一动不动。</p><p>因此得像NEHE框架一样靠我们自己另外&ldquo;安装&rdquo;一个含消息处理的死循环进去吗？其实我们可以在每次WM_PAINT消息被发送时执行的代码片段中依然加入渲染部分的代码，然后在代码末尾手动强迫让程序再次发送WM_PAINT消息：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt">&nbsp;</li>    <li><span class="comment">//在重绘消息WM_PAINT被发送时执行： </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CSampleView::OnDraw(CDC*&nbsp;pDC) </span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;CTSampleDoc*&nbsp;pDoc&nbsp;=&nbsp;GetDocument(); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;ASSERT_VALID(pDoc); </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//OPENGL前序工作顺利完成时，设置此标志为TRUE </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(!m_bAppIsActive) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;RenderAll();</span><span class="comment">//全部的渲染代码 </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;SwapBuffers(m_hDC);</span><span class="comment">//双缓冲交换 </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Invalidate(FALSE);&nbsp;</span><span class="comment">//发送WM_PAINT </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//在重绘消息WM_PAINT被发送时执行：<br/>void CSampleView::OnDraw(CDC* pDC)<br/>{<br/>	CTSampleDoc* pDoc = GetDocument();<br/>	ASSERT_VALID(pDoc);<br/><br/>//OPENGL前序工作顺利完成时，设置此标志为TRUE<br/>	if (!m_bAppIsActive)<br/>		return;<br/><br/>	RenderAll();//全部的渲染代码<br/><br/>	SwapBuffers(m_hDC);//双缓冲交换<br/><br/>	Invalidate(FALSE); //发送WM_PAINT<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>&nbsp;MFC中，VIEW类的成员函数OnDraw(CDC *pDC)实质是对回调响应OnPaint内部，BeginPaint和EndPaint之间那段代码的封装函数在VIEW类下的重载，因此其实就是程序的绘图函数。Invalidate的调用则是强制发送重绘消息，这样程序马上又进入本函数了&mdash;&mdash;这就通过MFC内置机制模拟了死循环这个大饼，另外，参数为FALSE是为了让GDI的背景重绘失效。</p><p>其他的都跟NEHE框架差不多了。像素设置，DC-RC关联和初始化放在void CSampleView::OnInitialUpdate()里就就可以了，反正就是让程序进入&ldquo;渲染循环&rdquo;之前完成，设m_bAppIsActive为TRUE。设置一个KillGLWindow()&nbsp;函数能让程序在设置失败或者程序关闭的时候能执行，以删除DC，RC和窗口~（上面提及的设置的定义和作用见[<a target="_blank" href="http://www.zwqxin.com/archives/MFC/3-nehe-frame-vc6.html.html">自剖一下自己用的NEHE OpenGL框架(下篇)</a>]）</p><p>补充一下，OPENGL的程序一定要放在CView类或由CView类派生的VIEW类中啊~！不知道为什么啊~！</p><p>最后走题一下，说说MFC分割窗口（这东西用WIN32做就苦了~）。在标准MFC的CMainFrame类里，利用CSplitterWnd对象作为成员变量（CSplitterWnd m_wndSplitter），对整个外观框架进行分割：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>BOOL&nbsp;CMainFrame::OnCreateClient(LPCREATESTRUCT&nbsp;lpcs,&nbsp;CCreateContext*&nbsp;pContext)&nbsp; </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(!m_wndSplitter.CreateStatic(</span><span class="keyword">this</span><span>,&nbsp;1,&nbsp;2))&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;FALSE;&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(!m_wndSplitter.CreateView(0,&nbsp;0,&nbsp;RUNTIME_CLASS(CLeftView),&nbsp;CSize(224,&nbsp;600),&nbsp;pContext)&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;||&nbsp;!m_wndSplitter.CreateView(0,&nbsp;1,&nbsp;RUNTIME_CLASS(CRightView),&nbsp;CSize(800,&nbsp;600),&nbsp;pContext))&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter.DestroyWindow();&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;FALSE;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;TRUE;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span class="comment">//&nbsp;&nbsp;return&nbsp;CFrameWnd::OnCreateClient(lpcs,&nbsp;pContext); </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) <br/>{<br/>	if (!m_wndSplitter.CreateStatic(this, 1, 2))    <br/>      return FALSE; <br/><br/>  if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CLeftView), CSize(224, 600), pContext) <br/>    || !m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CRightView), CSize(800, 600), pContext)) <br/>	{               <br/>	   m_wndSplitter.DestroyWindow(); <br/>	   return FALSE; <br/>	} <br/>	return TRUE; <br/>	<br/>//	return CFrameWnd::OnCreateClient(lpcs, pContext);<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>这样出来的是两个左右分割的VIEW（对应同一个DOCUMENT）。这样，我在左框架下的视图放个树控件，在右框架下的视图进行OPENGL绘图，好，好~</p><p>如果两个视图需要沟通呢？譬如在左视图树控件内点击某项（模型名称），右视图的模型便被选中（选中状态下可进行鼠标拖动模型在XY平面移动，嘛，这处理细节不重要）。这样的话，右视图的VIEW类起码得能够接收从左视图VIEW类发来的信息啊~它得知道有左视图的存在！我们在右视图里添加一个连接用的函数，在初始化时调用以建立与左视图的&ldquo;连接&rdquo;：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt">&nbsp;</li>    <li><span class="keyword">class</span><span>&nbsp;CLeftView; </span></li>    <li class="alt"><span class="keyword">class</span><span>&nbsp;CRightView&nbsp;:&nbsp;</span><span class="keyword">public</span><span>&nbsp;CView </span></li>    <li><span>{ </span></li>    <li class="alt"><span>.................. </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;CLeftView&nbsp;&nbsp;*MyTreeView; </span></li>    <li class="alt"><span>}; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">bool</span><span>&nbsp;CRightView&nbsp;::ConnectTreeView() </span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;CMainFrame&nbsp;*MyMainFrame&nbsp;=&nbsp;(CMainFrame&nbsp;*)AfxGetApp()-&gt;m_pMainWnd; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!MyMainFrame)</span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">false</span><span>; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;MyTreeView&nbsp;=&nbsp;(CLeftView&nbsp;&nbsp;*)MyMainFrame-&gt;m_wndSplitter.GetPane(0,0); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!MyTreeView)</span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">false</span><span>; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">true</span><span>; </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class CLeftView;<br/>class CRightView : public CView<br/>{<br/>..................<br/>	CLeftView  *MyTreeView;<br/>};<br/><br/>bool CRightView ::ConnectTreeView()<br/>{<br/>	CMainFrame *MyMainFrame = (CMainFrame *)AfxGetApp()-&gt;m_pMainWnd;<br/>	if(!MyMainFrame)return false;<br/><br/>	MyTreeView = (CLeftView  *)MyMainFrame-&gt;m_wndSplitter.GetPane(0,0);<br/>	if(!MyTreeView)return false;<br/><br/>	return true;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>在初始化时调用上函数后，现在MyTreeView这个成员指针变量就指向了左视图VIEW类（作为实例）了~注意GetPane的参数跟之前创建分割窗口的代码段中生成左视图时CreateView的头两个参数是一致的。</p><p>在一个之前弄的小DEMO里，有&ldquo;把OPENGL放入标准MFC框架&rdquo;以及分割窗口的例子：<br /><br/><a target="_blank" href="http://code.google.com/p/openglpickingdemo/">Picking Demo by zwqxin 2008.9 &amp; 2009.6 </a><br /><br/>（在 downloads下的 PickingDemo by ZwqXin.rar压缩包）</p><p>确实，发觉我已经穿越了 - -。</p>]]></description><category>VC/MFC</category><comments>http://www.zwqxin.com/archives/MFC/formal-mfc-in-opengl.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=62</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=62&amp;key=e7aeebba</trackback:ping></item><item><title>WIN32下的窗口子类化</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/MFC/sub-winproc-win32.html</link><pubDate>Fri, 24 Apr 2009 20:50:20 +0800</pubDate><guid>http://www.zwqxin.com/archives/MFC/sub-winproc-win32.html</guid><description><![CDATA[<p>什么是子类化?又是跟继承有关吗?有时候是我们太想当然了。父类与子类不是那么一个只存在于一种特定语言里的东西。从辨证学角度来瞎扯，父子关系是一般性与特殊性的有机结合口牙~........（瞎扯！）&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>事情是这样的，有一天，<a href="http://www.zwqxin.com/">ZwqXin</a>正在弄着些什么代码，然后弄着弄着，突然双拳紧握，一击打在桌子上，碎碎念：怎么办怎么办！原来......原来.......是编程遇到麻烦了啊：我弄了个对话框，然后在对话框里放了个Static控件，是用来在它上面画画的。真的算画画吧，简化一下&mdash;&mdash;就是在控件上有条直线，我通过鼠标在控件框框上左键单击+移动鼠标，来改变该直线的斜率属性。&mdash;&mdash;本来以为是简单的事，蓦然发现无法弄成功。归因，就是在消息处理的时候出问题了。</p><p>对话框自然得有一个消息处理函数DLGPROC来处理针对该对话框发生的事情吧，该STATIC控件隶属于本对话框，也容易得到其句柄和矩形位置大小属性。那么，我在DLGPROC里处理我所需要的WM_LBUTTONDOWN, WM_LBUTTONUP和WM_MOUSEMOVE 消息，判断鼠标位置是否在STATIC控件范围内（矩形框内），若是，就进行相应的直线斜率改变&amp;重画处理等处理，可不行吗？</p><p>不行，当鼠标在STATIC控件内无论干什么，都没有上述消息传入，完全捕捉不到，而移到STATIC控件范围外才有，但这不是我需要的啊。为什么呢？GOOGLE一下，都说STATIC控件无法响应单击等简单消息外的其他鼠标消息&mdash;&mdash;鼠标在它里面完全是死寂的。至于单击，也是在WM_COMMAND里接收：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>LRESULT&nbsp;CALLBACK&nbsp;DLGPROC(HWND&nbsp;hdlg,&nbsp;UINT&nbsp;msg,&nbsp;WPARAM&nbsp;wparam,&nbsp;LPARAM&nbsp;lparam) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;</span><span class="keyword">switch</span><span>(msg) </span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;........ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">case</span><span>&nbsp;WM_COMMAND </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(HIWORD(wparam)&nbsp;==&nbsp;STN_CLICKED&nbsp;&amp;&amp;&nbsp;LOWORD(wparam)&nbsp;==&nbsp;IDC_GSTATIC) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onMouseDown&nbsp;=&nbsp;</span><span class="keyword">true</span><span>; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetCursorPos(&amp;OriMousePt); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScreenToClient(StaticWND,&amp;OriMousePt); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;........... </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;......... </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>} </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">false</span><span>; </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>LRESULT CALLBACK DLGPROC(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)<br/>{<br/> switch(msg)<br/>{<br/>  ........<br/>     case WM_COMMAND<br/>     {<br/>        if(HIWORD(wparam) == STN_CLICKED &amp;&amp; LOWORD(wparam) == IDC_GSTATIC)<br/>		   { <br/>		   onMouseDown = true;<br/>		   GetCursorPos(&amp;OriMousePt);<br/>		   ScreenToClient(StaticWND,&amp;OriMousePt);<br/>		    ...........<br/>		    }<br/>  .........<br/>     }<br/>}<br/><br/>return false;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>STN_CLICKED是少数几个STATIC文本控件能默认接受的消息(事实上就STN_CLICKED, STN_DBLCLK, STN_ENABLE, STN_DISABLE等那么几个,后两个也没什么用..)，同时还得通过资源号识别是哪个STATIC控件发出的(这里我是IDC_GSTATIC)，通过wparam的高低位进来。</p><p>怎么办，MOUSEMOVE等消息难道&ldquo;消失&rdquo;了？不可能的，WINDOWS的消息处理机制不会故意去放漏这些消息。这只是微软自以为是的一道坎。继续GOOGLE，都是什么继承CSTATIC之类的说法。哎呀，我不想就这样转去MFC啊~~于是在CSDN发帖求救。马上有回帖了，第一条是&ldquo;子类化就可以了&rdquo;，第二条是&ldquo;惟有子类化了&rdquo;........子类化！</p><p>难道是继承吗？搞这么复杂！再向GOOGLE求救。看到这篇文章<a target="_blank" href="http://www.vckbase.com/document/viewdoc/?id=1343"><strong>解说Win32的窗口子类化</strong></a>，发现自己想错了。</p><p>所谓WIN32窗口的子类化，是说窗口内子窗口的消息被委托给一个&ldquo;子窗口消息处理函数&rdquo;来完成。对啊，父类子类不一定就是说C++里那个&ldquo;类&rdquo;呀！对话框是窗口，它里面的控件也是窗口&mdash;&mdash;子窗口[<a target="_blank" href="http://www.zwqxin.com/archives/MFC/windows-game-programer-2.html">WINDOWS游戏编程大师技巧笔记之&mdash;&mdash;WIN32编程</a>] 。但是，它的&ldquo;父亲&rdquo;DLGPROC都完成不了的事情，它（SUBPROC）能做到么？确实，MFC能做到的东西，WIN32没可能望天的（毕竟MFC只是....）。</p><p>原理上，父窗口在初始化（生成）的时候，就可以指定子窗口的消息处理函数。然后该子窗口就能捕获发生在父窗口的子窗口部分（这里就是那个STATIC框）处的事件，并首先处理。都说了，消息不会漏掉的嘛，只是STATIC的接收器默认被限制了。譬如说，我若还需要WM_PAINT消息的话，如果我在父窗口的消息处理函数和子窗口的消息处理函数里都放一个的话（事实上我后来也这么做了），该控件内的重画消息按正常地流入父窗口，但首先被子窗口的处理函数捕获处理，处理完后再回到父窗口消息处理渠被父窗口的消息处理函数处理。一切是这么实现的：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>WNDPROC&nbsp;ParentGPROC&nbsp;=&nbsp;NULL; </span></span></li>    <li><span>LRESULT&nbsp;CALLBACK&nbsp;SUBGPROC(HWND&nbsp;subhwnd,&nbsp;UINT&nbsp;msg,&nbsp;WPARAM&nbsp;wparam,&nbsp;LPARAM&nbsp;lparam); </span></li>    <li class="alt">&nbsp;</li>    <li><span>LRESULT&nbsp;CALLBACK&nbsp;DLGPROC(HWND&nbsp;hdlg,&nbsp;UINT&nbsp;msg,&nbsp;WPARAM&nbsp;wparam,&nbsp;LPARAM&nbsp;lparam)&nbsp; </span></li>    <li class="alt"><span>{&nbsp; </span></li>    <li><span>&nbsp;</span><span class="keyword">switch</span><span>(msg)&nbsp; </span></li>    <li class="alt"><span>{&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">case</span><span>&nbsp;WM_INITDIALOG:&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......... </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SubGPROC&nbsp;=&nbsp;(WNDPROC)SetWindowLong(StaticWND,&nbsp;GWL_WNDPROC,&nbsp;(LONG)SUBGPROC); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......... </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">break</span><span>; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;........&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">case</span><span>&nbsp;WM_COMMAND&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(HIWORD(wparam)&nbsp;==&nbsp;STN_CLICKED&nbsp;&amp;&amp;&nbsp;LOWORD(wparam)&nbsp;==&nbsp;IDC_GSTATIC)&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onMouseDown&nbsp;=&nbsp;</span><span class="keyword">true</span><span>;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetCursorPos(&amp;OriMousePt);&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScreenToClient(StaticWND,&amp;OriMousePt);&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...........&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; </span></li>    <li><span>&nbsp;&nbsp;.........&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">break</span><span>; </span></li>    <li class="alt"><span>}&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">false</span><span>;&nbsp; </span></li>    <li><span>} </span></li>    <li class="alt">&nbsp;</li>    <li><span>LRESULT&nbsp;CALLBACK&nbsp;SUBGPROC(HWND&nbsp;subhwnd,&nbsp;UINT&nbsp;msg,&nbsp;WPARAM&nbsp;wparam,&nbsp;LPARAM&nbsp;lparam) </span></li>    <li class="alt"><span>{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">switch</span><span>(msg) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">case</span><span>&nbsp;WM_PAINT: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SThdc&nbsp;=&nbsp;BeginPaint(StaticWND,&nbsp;&amp;ps); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;.............. </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EndPaint(StaticWND,&nbsp;&amp;ps); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;0; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">break</span><span>; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">case</span><span>&nbsp;WM_LBUTTONDOWN: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onMouseDown&nbsp;=&nbsp;</span><span class="keyword">true</span><span>;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetCursorPos(&amp;OriMousePt); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScreenToClient(subhwnd,&amp;OriMousePt); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......... </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">break</span><span>; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">case</span><span>&nbsp;WM_LBUTTONUP: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onMouseDown&nbsp;=&nbsp;</span><span class="keyword">false</span><span>; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">break</span><span>; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">case</span><span>&nbsp;WM_MOUSEMOVE: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(onMouseDown) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;POINT&nbsp;MosuePos; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetCursorPos(&amp;MosuePos); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScreenToClient(subhwnd,&amp;MosuePos); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.............. </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InvalidateRect(StaticWND,NULL,TRUE); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">break</span><span>; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">default</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;CallWindowProc(ParentGPROC&nbsp;,subhwnd,&nbsp;msg,&nbsp;wparam,&nbsp;lparam); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;0; </span></li>    <li class="alt"><span>} </span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>WNDPROC ParentGPROC = NULL;<br/>LRESULT CALLBACK SUBGPROC(HWND subhwnd, UINT msg, WPARAM wparam, LPARAM lparam);<br/><br/>LRESULT CALLBACK DLGPROC(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) <br/>{ <br/> switch(msg) <br/>{ <br/>        case WM_INITDIALOG: <br/>         .........<br/>       SubGPROC = (WNDPROC)SetWindowLong(StaticWND, GWL_WNDPROC, (LONG)SUBGPROC);<br/>        .........<br/>       break;<br/><br/>  ........ <br/>     case WM_COMMAND <br/>     { <br/>        if(HIWORD(wparam) == STN_CLICKED &amp;&amp; LOWORD(wparam) == IDC_GSTATIC) <br/>           {  <br/>           onMouseDown = true; <br/>           GetCursorPos(&amp;OriMousePt); <br/>           ScreenToClient(StaticWND,&amp;OriMousePt); <br/>            ........... <br/>            } <br/>  ......... <br/>     } <br/>   break;<br/>} <br/><br/>return false; <br/>}<br/><br/>LRESULT CALLBACK SUBGPROC(HWND subhwnd, UINT msg, WPARAM wparam, LPARAM lparam)<br/>{<br/>	switch(msg)<br/>	{<br/>	case WM_PAINT:<br/>          	SThdc = BeginPaint(StaticWND, &amp;ps);<br/>  				<br/>	..............<br/><br/>			EndPaint(StaticWND, &amp;ps);<br/>			return 0;<br/>	break;<br/>	<br/><br/>	case WM_LBUTTONDOWN:<br/>        onMouseDown = true;	<br/>		GetCursorPos(&amp;OriMousePt);<br/>		ScreenToClient(subhwnd,&amp;OriMousePt);<br/>		.........<br/>	break;<br/>	case WM_LBUTTONUP:<br/>		onMouseDown = false;<br/>	break;<br/>	case WM_MOUSEMOVE:<br/>		if(onMouseDown)<br/>		{<br/>			POINT MosuePos;<br/>			GetCursorPos(&amp;MosuePos);<br/>			ScreenToClient(subhwnd,&amp;MosuePos);<br/>	    	..............<br/>				 InvalidateRect(StaticWND,NULL,TRUE);<br/>		}<br/>	break;<br/>	default:<br/>		return CallWindowProc(ParentGPROC ,subhwnd, msg, wparam, lparam);<br/>	}<br/>	<br/><br/>	return 0;<br/>}<br/></pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>SetWindowLong接收一个GWL_WNDPROC的标志，表明是用参数内的SUBGPROC函数指针代替当前父窗口的消息处理，并返回父窗口消息处理函数的指针ParentGPROC。</p><p>子窗口消息处理函数最后的CallWindowProc(ParentGPROC,subhwnd,&nbsp;msg,&nbsp;wparam,&nbsp;lparam); 就是为了把处理权交还给父窗口处理函数。我不知道在初始化父窗口时建立的这种关系为什么能持续下去，但事实就是这样，也可以看成只是为了方便消息渠里的消息找消息处理函数指针。</p><p>上面那篇文章最后还说了些注意事项，也就是资源ID和SS_NOTIFY样式之类那些，也要好好注意喔。</p><p>总之，WIN32&ldquo;子类化&rdquo;成功，找你找得好辛苦哦！WM_MOUSEMOVE!</p>]]></description><category>VC/MFC</category><comments>http://www.zwqxin.com/archives/MFC/sub-winproc-win32.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=46</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=46&amp;key=390d0f8b</trackback:ping></item><item><title>Win32编程实现的一点小功能</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/MFC/know-a-little-about-win32.html</link><pubDate>Mon, 16 Mar 2009 21:48:45 +0800</pubDate><guid>http://www.zwqxin.com/archives/MFC/know-a-little-about-win32.html</guid><description><![CDATA[<p style="text-align: left">稍微记录一下这几天小小弄WIN32编程发现的一点点东西。发现是颇有乐趣的，可是烦恼往往出现在GOOGLE不出需要的资料以及试验不成功上。呵呵。上篇是<a target="_blank" href="http://www.zwqxin.com/archives/MFC/windows-game-programer-2.html">WINDOWS游戏编程大师技巧笔记之&mdash;&mdash;WIN32编程</a>&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>1. 透明的窗口</p><p>突如其来的想法，透明的窗口，怎么实现呢？GOGLE一下，出来了：SetLayeredWindowAttributes</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/MFC/know-a-little-about-win32.html"><img alt="Win32 小技巧" src="http://lh4.ggpht.com/_lYWT-2PnV0s/ScZc76NuEwI/AAAAAAAAAVM/MCdTg2bNgXs/s800/win32_1.jpg" /></a></p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">long</span><span>&nbsp;currentWindowLong&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_EXSTYLE); </span></span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_EXSTYLE,&nbsp;currentWindowLong&nbsp;|&nbsp;WS_EX_LAYERED);&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetLayeredWindowAttributes(hwnd,&nbsp;0,&nbsp;255&nbsp;*&nbsp;70/100,&nbsp;0x2);</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>			        long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);<br/>                    SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong | WS_EX_LAYERED);	<br/>	                SetLayeredWindowAttributes(hwnd, 0, 255 * 70/100, 0x2);</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>首先，我要用GetWindowLong获得当前窗口（句柄hwnd，我的主窗口）的某些设置，GWL_EXSTYLE是拓展风格。SetWindowLong直接看来就是把该设置改变了，这里的按位或有什么作用呢？很显然地是在原&ldquo;基础&rdquo;上加上一项WS_EX_LAYERED（分层特性）。只是为什么要是按位或呢？</p><p>好了，SetLayeredWindowAttributes第三个参数明显就是透明度了（满不透是255）。另两个是什么和为什么，不知道了。要想回去不用透明的世界，只要：（ 非 了之后再 与 么）</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">long</span><span>&nbsp;currentWindowLong&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_EXSTYLE); </span></span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_EXSTYLE,&nbsp;currentWindowLong&nbsp;&amp;&nbsp;~WS_EX_LAYERED);</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>		             long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);<br/>                     SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong &amp; ~WS_EX_LAYERED);</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>可是这样设置恐怕不成功吧。因为SetLayeredWindowAttributes是个拓展函数（至少在我这里，它是），我想这跟OPENGL的拓展机制差不多吧。而且在我看来它的解决之道也差不多。问题是一个关键词Module（组件），是说com么？不知道呢。在这里把它当拓展函数库就得了 - -。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>typedef&nbsp;BOOL&nbsp;(WINAPI&nbsp;*&nbsp;TRANSFUNC)(HWND&nbsp;hwnd,&nbsp;COLORREF&nbsp;crKey,&nbsp;BYTE&nbsp;bAlpha,&nbsp;DWORD&nbsp;dwFlags); </span></span></li>    <li><span>TRANSFUNC&nbsp;SetLayeredWindowAttributes; </span></li>    <li class="alt">&nbsp;</li>    <li><span>#define&nbsp;WS_EX_LAYERED&nbsp;0x00080000</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>typedef BOOL (WINAPI * TRANSFUNC)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);<br/>TRANSFUNC SetLayeredWindowAttributes;<br/><br/>#define WS_EX_LAYERED 0x00080000</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>这是获取函数入口吧，果然差不多。看这函数的签名式，第二个参数就是colorkey了，有什么用么，请知情者告解。最后一个是标志位。于是SetLayeredWindowAttributes就这么出来了，另外WS_EX_LAYERED也要给它&ldquo;真正&rdquo;的名字。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;LoadTransparenceModule() </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;HMODULE&nbsp;trModule&nbsp;=&nbsp;GetModuleHandle(</span><span class="string">&quot;User32.dll&quot;</span><span>); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;SetLayeredWindowAttributes&nbsp;=&nbsp;(TRANSFUNC)GetProcAddress(trModule,&nbsp;</span><span class="string">&quot;SetLayeredWindowAttributes&quot;</span><span>); </span></li>    <li class="alt">&nbsp;</li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void LoadTransparenceModule()<br/>{<br/>	HMODULE trModule = GetModuleHandle(&quot;User32.dll&quot;);<br/>	SetLayeredWindowAttributes = (TRANSFUNC)GetProcAddress(trModule, &quot;SetLayeredWindowAttributes&quot;);<br/><br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>所谓组件就是这个User32.dll嘛，取地址后，之前代码里的SetLayeredWindowAttributes有了意义。注意上面这函数是初始化里要有的（我放在WM_CREATE了）。</p><p>2. 判断鼠标是否位于客户区内，在客户区内外采取不同行为</p><p>最初我是这么做的：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">case</span><span>&nbsp;WM_MOUSEMOVE: </span></span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RECT&nbsp;MyRect; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetClientRect(hwnd,&nbsp;&amp;MyRect); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;POINT&nbsp;mousept; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mousept.x&nbsp;=&nbsp;LOWORD(lparam); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mousept.y&nbsp;=&nbsp;HIWORD(lparam); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!PtInRect(&amp;MyRect,mousept)) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">long</span><span>&nbsp;currentWindowLong&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_EXSTYLE); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_EXSTYLE,&nbsp;currentWindowLong&nbsp;|&nbsp;WS_EX_LAYERED);&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetLayeredWindowAttributes(hwnd,&nbsp;0,&nbsp;255&nbsp;*&nbsp;70/100,&nbsp;0x2); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">long</span><span>&nbsp;currentWindowLong&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_EXSTYLE); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_EXSTYLE,&nbsp;currentWindowLong&nbsp;&amp;&nbsp;~WS_EX_LAYERED); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span class="keyword">break</span><span>;</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>	case WM_MOUSEMOVE:<br/>		{<br/>			RECT MyRect;<br/>			GetClientRect(hwnd, &amp;MyRect);<br/><br/>			POINT mousept;<br/>			mousept.x = LOWORD(lparam);<br/>			mousept.y = HIWORD(lparam);<br/><br/>			if(!PtInRect(&amp;MyRect,mousept))<br/>			{<br/>			    long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);<br/>				SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong | WS_EX_LAYERED);	<br/>	            SetLayeredWindowAttributes(hwnd, 0, 255 * 70/100, 0x2);<br/>			}<br/>			else{<br/>		    	long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);<br/>                SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong &amp; ~WS_EX_LAYERED);<br/>			<br/>			}<br/>         <br/>		}<br/>	break;</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>我是想鼠标移到窗口外的时候，窗口变透明，移动回里面的时候变回不透明。当然想到自己熟悉的WM_MOUSEMOVE消息了，但是这是不行的：WM_MOUSEMOVE貌似只能处理客户区内部的消息，得到的鼠标坐标也是根植于客户区。于是我：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">case</span><span>&nbsp;WM_NCHITTEST: </span></span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RECT&nbsp;MyRect; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetClientRect(hwnd,&nbsp;&amp;MyRect); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;POINT&nbsp;mousept; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mousept.x&nbsp;=&nbsp;LOWORD(lparam); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mousept.y&nbsp;=&nbsp;HIWORD(lparam); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScreenToClient(hwnd,&nbsp;&amp;mousept); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!PtInRect(&amp;MyRect,mousept)) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">long</span><span>&nbsp;currentWindowLong&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_EXSTYLE); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_EXSTYLE,&nbsp;currentWindowLong&nbsp;|&nbsp;WS_EX_LAYERED);&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetLayeredWindowAttributes(hwnd,&nbsp;0,&nbsp;255&nbsp;*&nbsp;70/100,&nbsp;0x2); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetCapture(hwnd); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">long</span><span>&nbsp;currentWindowLong&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_EXSTYLE); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_EXSTYLE,&nbsp;currentWindowLong&nbsp;&amp;&nbsp;~WS_EX_LAYERED); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span class="keyword">break</span><span>;</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>	case WM_NCHITTEST:<br/>		{<br/>			RECT MyRect;<br/>			GetClientRect(hwnd, &amp;MyRect);<br/><br/>			POINT mousept;<br/>			mousept.x = LOWORD(lparam);<br/>			mousept.y = HIWORD(lparam);<br/><br/>			ScreenToClient(hwnd, &amp;mousept);<br/><br/>			if(!PtInRect(&amp;MyRect,mousept))<br/>			{<br/>			    long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);<br/>				SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong | WS_EX_LAYERED);	<br/>	            SetLayeredWindowAttributes(hwnd, 0, 255 * 70/100, 0x2);<br/>			}<br/>			else{<br/>				SetCapture(hwnd);<br/>		    	long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);<br/>                SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong &amp; ~WS_EX_LAYERED);<br/>			<br/>			}<br/>		}<br/>	break;</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>注意我是把窗口坐标变换回客户区坐标坐标跟客户区矩形做比较。SetCapture是个比较厉害的函数，它能为当前窗口跟踪鼠标，即使鼠标到了屏幕任何角落。当然一个时刻只能给一个窗口这种权利。传说有个比它厉害的钩子函数SetWindowsHookEx来着。但是这里我就用SetCapture了，在分支飞出else的时候看来是会直接ReleaseCapture的，加不加差不多&mdash;&mdash;重要的是，即使用了能针对整个屏幕判断鼠标的WM_NCHITTEST，也没有好结果&mdash;&mdash;我鼠标移动到按钮等等子控件上或者菜单栏后，都会ReleaseCapture&mdash;&mdash;也就是说它们&ldquo;不在窗口内&rdquo;。因为控件 == 窗口嘛。有没有其他简便方法呢？最后GOOGLE出可以是这么来着：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">case</span><span>&nbsp;WM_MOUSEMOVE: </span></span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!mouseTrack) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TRACKMOUSEEVENT&nbsp;mouseevent; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mouseevent.cbSize&nbsp;=&nbsp;</span><span class="keyword">sizeof</span><span>(TRACKMOUSEEVENT); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mouseevent.dwFlags&nbsp;=&nbsp;TME_LEAVE; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mouseevent.hwndTrack&nbsp;=&nbsp;hwnd; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(&nbsp;TrackMouseEvent(&amp;mouseevent)&nbsp;) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mouseTrack&nbsp;=&nbsp;</span><span class="keyword">true</span><span>; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">long</span><span>&nbsp;currentWindowLong&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_EXSTYLE); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_EXSTYLE,&nbsp;currentWindowLong&nbsp;&amp;&nbsp;~WS_EX_LAYERED); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">long</span><span>&nbsp;currentWinStyle&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_STYLE); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_STYLE,&nbsp;currentWinStyle&nbsp;|&nbsp;WS_CAPTION); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span class="keyword">break</span><span>; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">case</span><span>&nbsp;WM_MOUSELEAVE: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RECT&nbsp;MyRect; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetClientRect(hwnd,&nbsp;&amp;MyRect); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;POINT&nbsp;mousept; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetCursorPos(&amp;mousept); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScreenToClient(hwnd,&nbsp;&amp;mousept); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!PtInRect(&amp;MyRect,mousept)) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">long</span><span>&nbsp;currentWindowLong&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_EXSTYLE); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_EXSTYLE,&nbsp;currentWindowLong&nbsp;|&nbsp;WS_EX_LAYERED);&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetLayeredWindowAttributes(hwnd,&nbsp;0,&nbsp;255&nbsp;*&nbsp;70/100,&nbsp;0x2); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">long</span><span>&nbsp;currentWinStyle&nbsp;=&nbsp;GetWindowLong(hwnd,&nbsp;GWL_STYLE); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwnd,&nbsp;GWL_STYLE,&nbsp;currentWinStyle&nbsp;&amp;~&nbsp;WS_CAPTION); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mouseTrack&nbsp;=&nbsp;</span><span class="keyword">false</span><span>; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span class="keyword">break</span><span>;</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>	case WM_MOUSEMOVE:<br/>		{<br/><br/>			if(!mouseTrack)<br/>			{<br/>				TRACKMOUSEEVENT mouseevent;<br/>				mouseevent.cbSize = sizeof(TRACKMOUSEEVENT);<br/>				mouseevent.dwFlags = TME_LEAVE;<br/>				mouseevent.hwndTrack = hwnd;<br/><br/>				if( TrackMouseEvent(&amp;mouseevent) )<br/>				{<br/>					mouseTrack = true;<br/>					long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);<br/>                    SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong &amp; ~WS_EX_LAYERED);<br/><br/>					long currentWinStyle = GetWindowLong(hwnd, GWL_STYLE);<br/>					SetWindowLong(hwnd, GWL_STYLE, currentWinStyle | WS_CAPTION);<br/><br/>				}<br/>			}         <br/>		}<br/>	break;<br/><br/>	case WM_MOUSELEAVE:<br/>		{<br/>			RECT MyRect;<br/>			GetClientRect(hwnd, &amp;MyRect);<br/><br/>			POINT mousept;<br/>			GetCursorPos(&amp;mousept);<br/><br/>			ScreenToClient(hwnd, &amp;mousept);<br/><br/>			if(!PtInRect(&amp;MyRect,mousept))<br/>			{<br/>			long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);<br/>			SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong | WS_EX_LAYERED);	<br/>			SetLayeredWindowAttributes(hwnd, 0, 255 * 70/100, 0x2);<br/>	<br/>			long currentWinStyle = GetWindowLong(hwnd, GWL_STYLE);<br/>			SetWindowLong(hwnd, GWL_STYLE, currentWinStyle &amp;~ WS_CAPTION);<br/><br/>			}<br/>			mouseTrack = false;<br/>		}<br/>	break;</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>TRACKMOUSEEVENT！在鼠标离开某一窗口或在某一窗口上停留超过某一特定时间长度时发送消息（mouseevent.dwFlags&nbsp;有TME_LEAVE和TME_HOOK两种标志）。这专用的鼠标行为记录函数很是厉害，因为它能配合WM_MOUSELEAVE这种消息一起作用。窗口是子窗口还是控件已经没所谓了（但是菜单仍然不行，遗憾）。</p><p>这在我的VC6.0里，有些功能不被直接支持，其中就有WM_MOUSELEAVE和TRACKMOUSEEVENT。但实际上它还是支持的，在最程序开头（我这里是stdafx的#define WIN32_LEAN_AND_MEAN之上）加#define&nbsp;&nbsp; _WIN32_WINNT&nbsp;&nbsp; 0x0500就没问题了(XP明明是NT之上嘛...嘛嘛)。</p><p>另外必要吐槽一下的是标题栏的消隐~！我按透明那样类似的手续做，但是它只在最开始那次离开窗口有效（标题栏消失），但是再回去窗口内却看不见标题栏回来TAT。</p><p>3.调用控制台</p><p>别以为WIN32了就不能开控制台了。AllocConsole()能让你与控制台再次想见！</p><p style="text-align: center">&nbsp;<a target="_self" href="http://www.zwqxin.com/archives/MFC/know-a-little-about-win32.html"><img alt="Win32 小技巧" src="http://lh3.ggpht.com/_lYWT-2PnV0s/ScZc8sQWxEI/AAAAAAAAAVU/fJdjtPYqqaE/s800/win32_2.jpg" /></a></p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>AllocConsole(); </span></span></li>    <li><span>&nbsp;freopen(</span><span class="string">&quot;CONOUT$&quot;</span><span>,</span><span class="string">&quot;w+t&quot;</span><span>,stdout);&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;freopen(</span><span class="string">&quot;CONIN$&quot;</span><span>,</span><span class="string">&quot;r+t&quot;</span><span>,stdin);&nbsp; </span></li>    <li><span>&nbsp;&nbsp;printf(</span><span class="string">&quot;hello\n&quot;</span><span>);</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>  AllocConsole();<br/>   freopen(&quot;CONOUT$&quot;,&quot;w+t&quot;,stdout); <br/>    freopen(&quot;CONIN$&quot;,&quot;r+t&quot;,stdin); <br/>    printf(&quot;hello\n&quot;);</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>设置程这样就可以了。我在一个按钮上执行以上代码，调用成功。不过还是会有意外的（具体是什么不说了，不具有一般性）。然后关闭控制台只要FreeConsole()就可以了。注意直接关闭控制台是会连主窗口一起关闭的（两者一心同体~）。可以这样使用户不能手动关闭控制台，在上面代码两个fopen之后写上：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>GetConsoleTitle(buffer,&nbsp;&nbsp;80);&nbsp;&nbsp;&nbsp; </span></span></li>    <li><span>HWND&nbsp;&nbsp;hwnd&nbsp;&nbsp;=&nbsp;&nbsp;FindWindow(NULL,&nbsp;&nbsp;buffer);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>HMENU&nbsp;hmenu&nbsp;=&nbsp;&nbsp;GetSystemMenu(hwnd,&nbsp;&nbsp;FALSE);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>RemoveMenu(hmenu,&nbsp;SC_CLOSE,MF_BYCOMMAND); </span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>				GetConsoleTitle(buffer,  80);   <br/>				HWND  hwnd  =  FindWindow(NULL,  buffer);         <br/>				HMENU hmenu =  GetSystemMenu(hwnd,  FALSE);         <br/>				RemoveMenu(hmenu, SC_CLOSE,MF_BYCOMMAND);<br/></pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>这样就通过FindWindow以控制台标题为线索能找到该控制台，diable它的关闭按钮了。当然，你不能总让用户对着这个控制台无法关闭，人家会怒的。可以设定一个按钮或按键等等调用FreeConsole()。</p><p>4. XP菜单风格</p><p>VC6不能直接嵌入manifest脚本文件来改变样式，但是其实更简单：在RESOURCEVIEW里插入类型，选择自定义，资源类型填24。内容复制粘贴成以下的（它自己会转*进制的了），然后在资源属性里改其名字（ID）为1就可：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>&lt;?xml&nbsp;version=</span><span class="string">&quot;1.0&quot;</span><span>&nbsp;encoding=</span><span class="string">&quot;UTF-8&quot;</span><span>&nbsp;standalone=</span><span class="string">&quot;yes&quot;</span><span>?&gt; </span></span></li>    <li><span>&lt;assembly&nbsp;xmlns=</span><span class="string">&quot;urn:schemas-microsoft-com:asm.v1&quot;</span><span>&nbsp;manifestVersion=</span><span class="string">&quot;1.0&quot;</span><span>&gt; </span></li>    <li class="alt"><span>&lt;assemblyIdentity </span></li>    <li><span>&nbsp;&nbsp;name=</span><span class="string">&quot;XP&nbsp;style&nbsp;manifest&quot;</span></li>    <li class="alt"><span>&nbsp;&nbsp;processorArchitecture=</span><span class="string">&quot;x86&quot;</span></li>    <li><span>&nbsp;&nbsp;version=</span><span class="string">&quot;1.0.0.0&quot;</span></li>    <li class="alt"><span>&nbsp;&nbsp;type=</span><span class="string">&quot;win32&quot;</span><span>/&gt; </span></li>    <li><span>&lt;dependency&gt; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&lt;dependentAssembly&gt; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;assemblyIdentity </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type=</span><span class="string">&quot;win32&quot;</span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=</span><span class="string">&quot;Microsoft.Windows.Common-Controls&quot;</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;version=</span><span class="string">&quot;6.0.0.0&quot;</span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;processorArchitecture=</span><span class="string">&quot;x86&quot;</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;publicKeyToken=</span><span class="string">&quot;6595b64144ccf1df&quot;</span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;language=</span><span class="string">&quot;*&quot;</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;/&gt; </span></li>    <li><span>&nbsp;&nbsp;&lt;/dependentAssembly&gt; </span></li>    <li class="alt"><span>&lt;/dependency&gt; </span></li>    <li><span>&lt;/assembly&gt; </span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;yes&quot;?&gt;<br/>&lt;assembly xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot; manifestVersion=&quot;1.0&quot;&gt;<br/>&lt;assemblyIdentity<br/>  name=&quot;XP style manifest&quot;<br/>  processorArchitecture=&quot;x86&quot;<br/>  version=&quot;1.0.0.0&quot;<br/>  type=&quot;win32&quot;/&gt;<br/>&lt;dependency&gt;<br/>  &lt;dependentAssembly&gt;<br/>    &lt;assemblyIdentity<br/>      type=&quot;win32&quot;<br/>      name=&quot;Microsoft.Windows.Common-Controls&quot;<br/>      version=&quot;6.0.0.0&quot;<br/>      processorArchitecture=&quot;x86&quot;<br/>      publicKeyToken=&quot;6595b64144ccf1df&quot;<br/>      language=&quot;*&quot;<br/>    /&gt;<br/>  &lt;/dependentAssembly&gt;<br/>&lt;/dependency&gt;<br/>&lt;/assembly&gt;<br/></pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>最后是不成气候的尝试物：&nbsp;<a target="_blank" href="http://www.zwqxin.com/upload/2009/3/WindowZwq.rar">WindowZwq.rar</a></p>]]></description><category>VC/MFC</category><comments>http://www.zwqxin.com/archives/MFC/know-a-little-about-win32.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=41</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=41&amp;key=4e1b626b</trackback:ping></item><item><title>WINDOWS游戏编程大师技巧笔记之——WIN32编程</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/MFC/windows-game-programer-2.html</link><pubDate>Sat, 14 Mar 2009 20:01:19 +0800</pubDate><guid>http://www.zwqxin.com/archives/MFC/windows-game-programer-2.html</guid><description><![CDATA[<p>对于WIN32编程,我完全是小白中的小白。今天小白有感想发表~~。此为WINDOWS游戏编程大师技巧笔记之二，WIN32编程。为啥要学啊？啊....那个.......貌似......不算学吧.....呵呵。上篇的笔记在这里，<a target="_blank" href="http://www.zwqxin.com/archives/cpp/windows-game-programer-1.html">WINDOWS游戏编程大师技巧笔记之&mdash;&mdash;学海无崖</a>。</p><p>上一次谈及MFC貌似是刚开博客的时候了吧，上次是为了OPENGL框架剖析，这次呢？恩，或者恰巧碰上一个小编程作业的缘故，而又恰好看《WINDOWS游戏编程大师技巧》看到第2，3，4章左右的机缘吧。怎么说，不可能把这作业做在OPENGL框架上吧，那...普通的控制台输出也不错，但是这样的话什么都学不了了，不是吗？</p><p>上次的MFC：<br /><br/><a target="_blank" href="http://www.zwqxin.com/archives/MFC/nehe-frame-vc6.html">自剖一下自己用的NEHE OpenGL框架(上篇)</a><br /><br/><a target="_blank" href="http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html">自剖一下自己用的NEHE OpenGL框架(中篇)</a><br /><br/><a target="_blank" href="http://www.zwqxin.com/archives/MFC/3-nehe-frame-vc6.html.html">自剖一下自己用的NEHE OpenGL框架(中篇)</a><br /><br/>&nbsp;</p><p>好啦，我最初是跟随那书敲的代码，这样比直接复制粘贴感觉好多了。从第二章，一个简单的窗口的构建开始，一路附加一点功能。书上有的，想试试，书上没的想到的，就GOOGLE一下，相信只有我没想到的，没有它做不到的吧。WIN32算是比较底层的编程开发了，觉得也早被潮流冲得远去了，不说MFC，就在今天大行其道的.NET开发，人们还记得这个东西吗？大抵更多人接触它是大学学习的时候吧&mdash;&mdash;哪怕MFC更受欢迎。恩，今天怎么废话连篇？因为预感接下来写不了什么东西了&mdash;&mdash;WIN32，即使是最简单的那些东西，对我来说都是新事物，这几天接触了那么多&ldquo;新事物&rdquo;&mdash;&mdash;实在太多了，一一记录起来会累死的。还是把更多时间留去做些其他的吧。</p><p>话说我最开始做的是&ldquo;填&rdquo;一个窗口类：WNDCLASSEX winclass 。感觉就是跟&ldquo;填&rdquo;像素格式那样吧。实质就是给一个窗口类所需的属性，方便接下来创建这个类的实例。RegisterClassEx(&amp;winclass)注册这部在我通常的知识范围之外，当然了，要跟WINDOWS打交道起码要多点跟系统交互吧。好了，CreateWindowEx创建窗口是第三步&mdash;&mdash;最基本的三部。在WinMain里，为了维持这个窗口，需要一个while(TRUE)的循环。在这个循环里面，我们做所有的&ldquo;变动&rdquo;，OPENGL框架里是渲染，可真实的窗口不只会渲染哦。当然了，这几天其实也没在里面弄什么（还没开始GDI呢）~主要弄的是每次循环开始时刻的消息处理：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">if</span><span>(PeekMessage(&amp;MyMessage,&nbsp;NULL,&nbsp;0,0,PM_REMOVE)) </span></span></li>    <li><span>&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;</span><span class="keyword">if</span><span>(MyMessage.message&nbsp;==&nbsp;WM_QUIT)</span><span class="keyword">break</span><span>; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;TranslateMessage(&amp;MyMessage); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;DispatchMessage(&amp;MyMessage); </span></li>    <li class="alt"><span>&nbsp;&nbsp;}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>	 if(PeekMessage(&amp;MyMessage, NULL, 0,0,PM_REMOVE))<br/>	   {<br/>		  if(MyMessage.message == WM_QUIT)break;<br/><br/>		   TranslateMessage(&amp;MyMessage);<br/>		   DispatchMessage(&amp;MyMessage);<br/>	   }</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>PeekMessage和GetMessage的区别终于有感性认识了：不加while(TRUE)前如果直接PeekMessage，窗口闪一下就没掉了；而GetMessage则不会&mdash;&mdash;就貌似scanf给控制台断点，它也给WINMAIN函数断点了&mdash;&mdash;苦等消息而不立即返回。姑且把WINMAIN里的这PART&ldquo;死&rdquo;循环作为第四PART吧，那么第五PART无疑是WindowProc消息处理函数了。MFC里的那些消息映射的都是包装它的吧。CALLBACK关键字给了它脱不去的属性。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>LRESULT&nbsp;CALLBACK&nbsp;WindowProc(HWND&nbsp;hwnd,&nbsp;UINT&nbsp;msg,&nbsp;WPARAM&nbsp;wparam,&nbsp;LPARAM&nbsp;lparam)</span></span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>msg就是消息类型：就是常见的WM_PAINT，WM_SIZE那些。不提这些的话，相最有型的还是WM_COMMAND消息了。以前没什么感性认识是因为我们从来不真正相遇。WM_COMMAND消息可以处理纷繁的按钮和菜单点击消息。创建按钮和菜单，当然跃跃想试了。菜单，最开始在填表那里改，改不成功，还是LoadMenu，SetMenu来得老实。我直接放在窗口创建之后了。资源装载部分，其实VC6自己可以作为向导弄菜单，但是正如作者所说：那样你就什么都学不了了，不是么？事实上只是动手多一点而已，跟图标差不多吧。.rc就叫脚本文件最近才知道，但是编译器&ldquo;窗口&rdquo;里如果见它（表明它按文本形式打开了），记得关掉哦，否则提示&ldquo;资源装载失败&rdquo;。</p><p>按钮总是那么气人。怎么显示按钮被按下去那状态现在我还没GOOGLE出来。还有编辑框也恼人，响应那么慢~~还有那标题栏哦，怎么我弄不好那种自动隐藏的效果呢？</p><p>还有很多呢。接下来会分享一点偶尔发现的&ldquo;小技巧&rdquo;来实现一点功能。这就不属于笔记了。下篇吧。</p>]]></description><category>VC/MFC</category><comments>http://www.zwqxin.com/archives/MFC/windows-game-programer-2.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=40</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=40&amp;key=b6d9fd18</trackback:ping></item><item><title>自剖一下自己用的NEHE OpenGL框架(下篇)</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/MFC/3-nehe-frame-vc6.html.html</link><pubDate>Fri, 23 Jan 2009 13:28:37 +0800</pubDate><guid>http://www.zwqxin.com/archives/MFC/3-nehe-frame-vc6.html.html</guid><description><![CDATA[<p>最后一篇的&quot;自剖&quot;。本篇是:自剖一下自己用的NEHE OpenGL框架(下篇)，可能涉及更多的关于OpenGL与MFC、系统间的关联。</p><p>之前一直觉得，为了逃避MFC那一套，对自己用了这么长时间的狂架却没有个很好的了解，真惭愧。于是这三篇傻傻的文章就当作自己&nbsp;更好地了解NEHE OpenGL框架，同时学习一下MFC咯，自我修炼。（友情提醒:如果你只为OPENGL初学者之初学者,而不懂MFC也不想懂MFC,建议碰到类似的文章先别看[包括本文],更有益的是先关注渲染部分^^)<br /><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上两篇日志请看：<a target="_blank" href="http://www.zwqxin.com/archives/MFC/nehe-frame-vc6.html">自剖一下自己用的NEHE OpenGL框架(上篇)</a><br /><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ：<a target="_blank" href="http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html">自剖一下自己用的NEHE OpenGL框架(中篇)</a></p><p>好了，继续探讨CMainframe类的实现：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>CMainFrame::CMainFrame()&nbsp;&nbsp;&nbsp;</span><span class="comment">//构造函数 </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...................</span><span class="comment">//这里对应，向导为我们设定了头文件中变量相应初始值，某些可在向导里更改，我一向用1024*768，depth：32 </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;开始渲染前问问是否需要全屏幕，MessageBox函数参数一目了然 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(MessageBox(</span><span class="string">&quot;Would&nbsp;You&nbsp;Like&nbsp;To&nbsp;Run&nbsp;In&nbsp;Fullscreen&nbsp;Mode?&quot;</span><span>,&nbsp;</span><span class="string">&quot;Start&nbsp;FullScreen?&quot;</span><span>,MB_YESNO|MB_ICONQUESTION)==IDYES) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_bFullScreen=TRUE; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;加自定义的变量初始值于此 </span></li>    <li class="alt"><span>} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>CMainFrame::~CMainFrame()&nbsp;&nbsp;</span><span class="comment">//析构，调用KillGLWindow() </span></li>    <li><span>{&nbsp;&nbsp;&nbsp;KillGLWindow();&nbsp;&nbsp;}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>CMainFrame::CMainFrame()   //构造函数<br/>{<br/>         ...................//这里对应，向导为我们设定了头文件中变量相应初始值，某些可在向导里更改，我一向用1024*768，depth：32<br/><br/>	// 开始渲染前问问是否需要全屏幕，MessageBox函数参数一目了然<br/>	if (MessageBox(&quot;Would You Like To Run In Fullscreen Mode?&quot;, &quot;Start FullScreen?&quot;,MB_YESNO|MB_ICONQUESTION)==IDYES)<br/>	{          m_bFullScreen=TRUE;<br/>	// 加自定义的变量初始值于此<br/>}<br/><br/>CMainFrame::~CMainFrame()  //析构，调用KillGLWindow()<br/>{	KillGLWindow();  }</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>接下来的就比较重要了：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="comment">//在建立一个窗口前，你总得做些什么。从第一行代码看会觉得它带来了CFrameWnd的一切。但是其实它们的本源还是&nbsp;CWnd（重载于它）。窗口始终要被附加(attach)到一个CWnd对象上操作，CWnd会最终创建窗口 </span></span></li>    <li><span>BOOL&nbsp;CMainFrame::PreCreateWindow(CREATESTRUCT&amp;&nbsp;cs) </span></li>    <li class="alt"><span>{ </span></li>    <li><span class="comment">//CFrameWnd的PreCreateWindow究竟做了什么？太底层了我现在不想追查下去 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(!CFrameWnd::PreCreateWindow(cs))&nbsp; </span></li>    <li><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;</span><span class="keyword">return</span><span>&nbsp;FALSE; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//EnumDisplaySettings获得当前显示驱动所支持的所有显示模式，这里是指渲染窗口未出现时我们的屏幕设置。存储到上文提到过的m_DMsaved中 </span></li>    <li class="alt"><span class="comment">//ChangeDisplaySettings则是相应的改变显示模式了（见稍下方） </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;EnumDisplaySettings(NULL,&nbsp;ENUM_CURRENT_SETTINGS,&nbsp;&amp;m_DMsaved);&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span class="comment">//全屏幕时候的设置：临时搭建一个DEVMODE对象并给予它&ldquo;我们所定义的屏幕设置&rdquo;的信息，把它的显示模式&ldquo;拉伸&rdquo;改变为全屏幕（注意，屏幕还是我自己设定那个1024*768*32，只是被&ldquo;拉伸&rdquo;了，事实上我自己屏幕就是1024*768，拉不拉没所谓，不过全屏下退出只有按ESC键了） </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(m_bFullScreen) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEVMODE&nbsp;dmScreenSettings; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dmScreenSettings.dmPelsWidth&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;VB_WIDTH; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dmScreenSettings.dmPelsHeight&nbsp;&nbsp;&nbsp;=&nbsp;VB_HEIGHT; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dmScreenSettings.dmBitsPerPel&nbsp;&nbsp;&nbsp;=&nbsp;VB_DEPTH; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(ChangeDisplaySettings(&amp;dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;如果改变失败时执行的操作，无非是提示用户想不想退出程序之类，向导给我们做好了} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;参入的CREATESTRUCT结构的cs，在CWnd中定义了创建函数创建窗口所用的初始参数（窗口风格），成员变量cx,cy就是窗口宽高了 </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.cx&nbsp;=&nbsp;VB_WIDTH; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.cy&nbsp;=&nbsp;VB_HEIGHT; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(m_bFullScreen)</span><span class="comment">//全屏时还要加些样式 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.dwExStyle=WS_EX_APPWINDOW;</span><span class="comment">//这个叫拓展样式,你查下,很傻B的&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.style=WS_POPUP;</span><span class="comment">//&nbsp;&nbsp;&nbsp;&nbsp;猥琐(?)地弹出 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ShowCursor(FALSE);</span><span class="comment">//隐藏鼠标 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.dwExStyle=WS_EX_APPWINDOW&nbsp;|&nbsp;WS_EX_WINDOWEDGE;</span><span class="comment">//突起边缘 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.style=WS_OVERLAPPEDWINDOW;</span><span class="comment">//一个有标题栏的窗口,不猥琐 </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//cs.y和cs.x是新窗口的左上角Y,X坐标,GetSystemMetrics(SM_CYSCREEN)获得屏幕高,下面那个就是屏幕宽了.计算结果窗口就位于屏幕中心了 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.y&nbsp;=&nbsp;(</span><span class="keyword">int</span><span>)&nbsp;GetSystemMetrics(SM_CYSCREEN)&nbsp;/&nbsp;2&nbsp;-&nbsp;cs.cy&nbsp;/&nbsp;2;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.x&nbsp;=&nbsp;(</span><span class="keyword">int</span><span>)&nbsp;GetSystemMetrics(SM_CXSCREEN)&nbsp;/&nbsp;2&nbsp;-&nbsp;cs.cx&nbsp;/&nbsp;2; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span class="comment">//LPCSTR变量lpszClass是新窗口的窗口类名,默认的CS_OWNDC说明窗口有自己的DC.另外里面还嵌套了个LoadCursor(第一参数是NULL,说明LOAD的系统光标I,DC_ARROW表示指针) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;cs.lpszClass&nbsp;=&nbsp;AfxRegisterWndClass(CS_OWNDC|CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LoadCursor(NULL,IDC_ARROW),&nbsp;NULL,&nbsp;NULL); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;TRUE; </span></li>    <li><span>} </span></li>    <li class="alt">&nbsp;</li>    <li><span>#ifdef&nbsp;_DEBUG&nbsp;&nbsp; </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CMainFrame::AssertValid()&nbsp;</span><span class="keyword">const</span><span>&nbsp;</span><span class="comment">//这些DEBUG函数<a target="_blank" href="http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html">在中篇</a>说过了 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CFrameWnd::AssertValid();} </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CMainFrame::Dump(CDumpContext&amp;&nbsp;dc)&nbsp;</span><span class="keyword">const</span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CFrameWnd::Dump(dc);} </span></li>    <li class="alt"><span>#endif&nbsp;//_DEBUG</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//在建立一个窗口前，你总得做些什么。从第一行代码看会觉得它带来了CFrameWnd的一切。但是其实它们的本源还是 CWnd（重载于它）。窗口始终要被附加(attach)到一个CWnd对象上操作，CWnd会最终创建窗口<br/>BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&amp; cs)<br/>{<br/>//CFrameWnd的PreCreateWindow究竟做了什么？太底层了我现在不想追查下去<br/>	if (!CFrameWnd::PreCreateWindow(cs)) <br/>                                    return FALSE;<br/><br/>//EnumDisplaySettings获得当前显示驱动所支持的所有显示模式，这里是指渲染窗口未出现时我们的屏幕设置。存储到上文提到过的m_DMsaved中<br/>//ChangeDisplaySettings则是相应的改变显示模式了（见稍下方）<br/>	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &amp;m_DMsaved);	<br/>				<br/>//全屏幕时候的设置：临时搭建一个DEVMODE对象并给予它&ldquo;我们所定义的屏幕设置&rdquo;的信息，把它的显示模式&ldquo;拉伸&rdquo;改变为全屏幕（注意，屏幕还是我自己设定那个1024*768*32，只是被&ldquo;拉伸&rdquo;了，事实上我自己屏幕就是1024*768，拉不拉没所谓，不过全屏下退出只有按ESC键了）<br/>	if (m_bFullScreen)<br/>	{<br/>		DEVMODE dmScreenSettings;<br/>		dmScreenSettings.dmPelsWidth	= VB_WIDTH;<br/>		dmScreenSettings.dmPelsHeight	= VB_HEIGHT;<br/>		dmScreenSettings.dmBitsPerPel	= VB_DEPTH;<br/>		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;<br/><br/>		if (ChangeDisplaySettings(&amp;dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)<br/>		{  如果改变失败时执行的操作，无非是提示用户想不想退出程序之类，向导给我们做好了}<br/>	}<br/><br/><br/>	// 参入的CREATESTRUCT结构的cs，在CWnd中定义了创建函数创建窗口所用的初始参数（窗口风格），成员变量cx,cy就是窗口宽高了<br/><br/>	cs.cx = VB_WIDTH;<br/>	cs.cy = VB_HEIGHT;<br/><br/>	if (m_bFullScreen)//全屏时还要加些样式<br/>	{			<br/>	cs.dwExStyle=WS_EX_APPWINDOW;//这个叫拓展样式,你查下,很傻B的	<br/>	cs.style=WS_POPUP;//	猥琐(?)地弹出<br/>	ShowCursor(FALSE);//隐藏鼠标<br/>	}	<br/>	else<br/>	{	<br/>	cs.dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;//突起边缘<br/>	cs.style=WS_OVERLAPPEDWINDOW;//一个有标题栏的窗口,不猥琐<br/><br/>//cs.y和cs.x是新窗口的左上角Y,X坐标,GetSystemMetrics(SM_CYSCREEN)获得屏幕高,下面那个就是屏幕宽了.计算结果窗口就位于屏幕中心了<br/>	cs.y = (int) GetSystemMetrics(SM_CYSCREEN) / 2 - cs.cy / 2; <br/>	cs.x = (int) GetSystemMetrics(SM_CXSCREEN) / 2 - cs.cx / 2;<br/>	}<br/>//LPCSTR变量lpszClass是新窗口的窗口类名,默认的CS_OWNDC说明窗口有自己的DC.另外里面还嵌套了个LoadCursor(第一参数是NULL,说明LOAD的系统光标I,DC_ARROW表示指针)<br/>	cs.lpszClass = AfxRegisterWndClass(CS_OWNDC|CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,<br/>		LoadCursor(NULL,IDC_ARROW), NULL, NULL);<br/>	return TRUE;<br/>}<br/><br/>#ifdef _DEBUG  <br/>void CMainFrame::AssertValid() const //这些DEBUG函数在中篇说过了<br/>        {CFrameWnd::AssertValid();}<br/>void CMainFrame::Dump(CDumpContext&amp; dc) const<br/>        {CFrameWnd::Dump(dc);}<br/>#endif //_DEBUG</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>除了初始化窗口样式,还有更重要的就是OnCreateClient重载函数。它Cwnd建窗时执行 OnCreate（）时候被自动调用。一般告诉你别改，但是这里是为了加一些另外的设置，实际上第一条语句就是调用上级类的重载版了，参数也没拿来改。恩，我们是为了在建立窗口的同时希望系统附带上我们给OPENGL的一些设置。而这些设置很重要，是OPENGL与系统设备的交流，也是它注定比DirectX更高移植性的体现，这里只是一个与WINDOWS设备交互的例子而已。</p><p>我们在<a target="_blank" href="http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html">上篇文章</a>讲过DC和<font size="2">渲染描述句柄</font>RC。正确点来说它们就是两块内存。内存当然要来存东西。<br /><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 设备描述句柄（DC）：一个定义一组图形对象及其属性、影响输出的图形方式(数据)结构。它是WINDOWS提供的关于我们目前计算机上的设备的信息。你要在屏幕上搞东西啊？行！不过得问过我们的显示器屏幕（获得屏幕的DC）先。<br /><br/><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;渲染描述句柄（</font>RC）：这个就是属于我们OPENGL的了。它定义了一系列渲染相关信息，例如下面你将看到的像素信息。只有一个现行RC存在（对应于一个线程，唯一），OPENGL才会工作。<br /><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DC和RC的交互，因此也就是OPENGL与WINDOWS系统设备的交流。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>BOOL&nbsp;CMainFrame::OnCreateClient(LPCREATESTRUCT&nbsp;lpcs,&nbsp;CCreateContext*&nbsp;pContext) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;我们先调用&ldquo;真正的&rdquo;OnCreateClient，成功了才来做我们的设置 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;BOOL&nbsp;bRet&nbsp;=&nbsp;CFrameWnd::OnCreateClient(lpcs,&nbsp;pContext); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(bRet) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GLuint&nbsp;&nbsp;PixelFormat;&nbsp;</span><span class="comment">//用来存储所选择的像素信息 </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//PIXELFORMATDESCRIPTOR结构类型ptd描述了屏幕上所有像素的格式，可以说是一个描述器&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">static</span><span>&nbsp;&nbsp;PIXELFORMATDESCRIPTOR&nbsp;pfd=&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">sizeof</span><span>(PIXELFORMATDESCRIPTOR),</span><span class="comment">//这个描述器大小&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//版本 </span></li>    <li><span class="comment">//以下三项表明兼容性：这种像素格式要符合WINDOWS窗口，OPENGL&nbsp;&nbsp;&nbsp;，双缓冲（可选）标准&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PFD_DRAW_TO_WINDOW&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PFD_SUPPORT_OPENGL&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PFD_DOUBLEBUFFER, </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PFD_TYPE_RGBA,</span><span class="comment">//颜色格式RGBA </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VB_DEPTH,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//颜色深度就用屏幕深度 </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//然后进行一些像素规格设置，有些也不知道是什么来的，我用原来向导给的英文注释标明防歧义 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;&nbsp;&nbsp;</span><span class="comment">//Color&nbsp;Bits&nbsp;Ignored&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;&nbsp;</span><span class="comment">//alpha通道缓冲，不设置&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;Shift&nbsp;Bit&nbsp;Ignored&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;&nbsp;</span><span class="comment">//累积缓冲，OPENGL五大缓存之一，需要时可启用 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,</span><span class="comment">//&nbsp;Accumulation&nbsp;Bits&nbsp;Ignored </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16,&nbsp;</span><span class="comment">//设置16位深度缓冲，五大缓存之一 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;</span><span class="comment">//蒙板缓冲，五大缓存之一，需要时可启用 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;</span><span class="comment">//辅助缓冲，五大缓存之一，需要时可启用 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PFD_MAIN_PLANE,</span><span class="comment">//&nbsp;Main&nbsp;Drawing&nbsp;Layer(主绘图区)&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;</span><span class="comment">//保留&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;0,&nbsp;0&nbsp;</span><span class="comment">//Layer&nbsp;Masks&nbsp;Ignored&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};&nbsp; </span></li>    <li class="alt"><span class="comment">//注,五大缓存还有一个是颜色缓存(都设RGBA了,肯定有的,不然渲染窗口显示个啥)&nbsp;&nbsp; </span></li>    <li><span class="comment">//下面就是像素设置了(注:以下{...}部分无非威胁我们设置不成功会如何如何(其实很必要的)) </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//先由Cwnd为我们获得DC,存入CMainframe的变量m_hDC&nbsp;中: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!(&nbsp;m_hDC&nbsp;=&nbsp;::GetDC&nbsp;(&nbsp;m_hWnd&nbsp;)&nbsp;)&nbsp;)&nbsp;{.......}&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//根据DC和刚才的像素描述器选择像素格式,返回像素格式索引号,存入刚才定义的PixelFormat&nbsp;中 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!(&nbsp;PixelFormat&nbsp;=&nbsp;ChoosePixelFormat&nbsp;(&nbsp;m_hDC,&nbsp;&amp;pfd&nbsp;)&nbsp;)&nbsp;)&nbsp;{.......} </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//根据DC,描述表和像素格式索引号设置像素格式: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!SetPixelFormat&nbsp;(&nbsp;m_hDC,&nbsp;PixelFormat,&nbsp;&amp;pfd&nbsp;)&nbsp;{.......} </span></li>    <li>&nbsp;</li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//下面是管理RC.先说一下用来管理RC的WGL函数(WGL&nbsp;&ndash;&nbsp;Windows的&nbsp;OpenGL扩展层): </span></li>    <li class="alt"><span>※&nbsp;HGLRC&nbsp;wglCreateContext(HDC&nbsp;hdc)&nbsp;&nbsp;</span><span class="comment">//设置完像素格式后调用,根据DC创建一个RC </span></li>    <li><span>※BOOL&nbsp;wglMakeCurrent(HDC&nbsp;hdc,&nbsp;HGLRC&nbsp;hglrc)&nbsp;</span><span class="comment">//关联DC和RC,让RC成为现行(激活)的 </span></li>    <li class="alt"><span>※HGLRC&nbsp;wglGetCurrentContext()&nbsp;</span><span class="comment">//返回现行RC </span></li>    <li><span>※HDC&nbsp;wglGetCurrentDC()&nbsp;</span><span class="comment">//返回与现行RC关联的那个DC </span></li>    <li class="alt"><span>※BOOL&nbsp;wglDeleteContext(HGLRC&nbsp;hglrc)&nbsp;</span><span class="comment">//删除一个RC(最好先让它变成非现行状态) </span></li>    <li><span>※BOOL&nbsp;wglUseFontBitmaps(HDC&nbsp;hdc,&nbsp;DWORD&nbsp;dwFirst,&nbsp;DWORD&nbsp;dwCount,&nbsp;DWORD&nbsp;dwBase)&nbsp;</span><span class="comment">//这个用来根据DC创建字符显示列表 </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//下面需要用到最重要的第1,2个,至于第5个(删除)在后面退出时用: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!(&nbsp;m_hRC&nbsp;=&nbsp;wglCreateContext&nbsp;(&nbsp;m_hDC&nbsp;)&nbsp;)&nbsp;)&nbsp;{.......} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!wglMakeCurrent&nbsp;(&nbsp;m_hDC,&nbsp;m_hRC&nbsp;)&nbsp;)&nbsp;{.......} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!InitGL&nbsp;()&nbsp;)&nbsp;{.......}&nbsp;&nbsp;</span><span class="comment">//最后还要处理opengl的初始化 </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;m_bAppIsActive&nbsp;=&nbsp;TRUE;</span><span class="comment">//以上全成功了,可以渲染了! </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;bRet;&nbsp;</span><span class="comment">//没错的话,现在可以打开窗口了 </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)<br/>{<br/>	// 我们先调用&ldquo;真正的&rdquo;OnCreateClient，成功了才来做我们的设置<br/>	BOOL bRet = CFrameWnd::OnCreateClient(lpcs, pContext);<br/>	if (bRet)<br/>	{<br/>		GLuint	PixelFormat; //用来存储所选择的像素信息<br/><br/>//PIXELFORMATDESCRIPTOR结构类型ptd描述了屏幕上所有像素的格式，可以说是一个描述器	<br/>		static	PIXELFORMATDESCRIPTOR pfd=	 <br/>		{<br/>			sizeof(PIXELFORMATDESCRIPTOR),//这个描述器大小	<br/>			1,			   //版本<br/>//以下三项表明兼容性：这种像素格式要符合WINDOWS窗口，OPENGL	，双缓冲（可选）标准	<br/>			PFD_DRAW_TO_WINDOW |	<br/>			PFD_SUPPORT_OPENGL |	<br/>			PFD_DOUBLEBUFFER,<br/>		<br/>			PFD_TYPE_RGBA,//颜色格式RGBA<br/>			VB_DEPTH,            //颜色深度就用屏幕深度<br/><br/>//然后进行一些像素规格设置，有些也不知道是什么来的，我用原来向导给的英文注释标明防歧义<br/>			0, 0, 0, 0, 0, 0,   //Color Bits Ignored		<br/>			0,   //alpha通道缓冲，不设置		<br/>			0,   // Shift Bit Ignored	<br/>			0,   //累积缓冲，OPENGL五大缓存之一，需要时可启用<br/>			0, 0, 0, 0,// Accumulation Bits Ignored<br/>			16, //设置16位深度缓冲，五大缓存之一<br/>			0, //蒙板缓冲，五大缓存之一，需要时可启用<br/>			0, //辅助缓冲，五大缓存之一，需要时可启用<br/>			PFD_MAIN_PLANE,// Main Drawing Layer(主绘图区)	<br/>			0,	//保留		<br/>			0, 0, 0	//Layer Masks Ignored	<br/>		}; <br/>//注,五大缓存还有一个是颜色缓存(都设RGBA了,肯定有的,不然渲染窗口显示个啥)	<br/>//下面就是像素设置了(注:以下{...}部分无非威胁我们设置不成功会如何如何(其实很必要的))<br/><br/>//先由Cwnd为我们获得DC,存入CMainframe的变量m_hDC 中:<br/>	if ( !( m_hDC = ::GetDC ( m_hWnd ) ) ) {.......} <br/><br/>//根据DC和刚才的像素描述器选择像素格式,返回像素格式索引号,存入刚才定义的PixelFormat 中<br/>	if ( !( PixelFormat = ChoosePixelFormat ( m_hDC, &amp;pfd ) ) )	{.......}<br/><br/>//根据DC,描述表和像素格式索引号设置像素格式:<br/>	if ( !SetPixelFormat ( m_hDC, PixelFormat, &amp;pfd ) {.......}<br/><br/><br/>//下面是管理RC.先说一下用来管理RC的WGL函数(WGL &ndash; Windows的 OpenGL扩展层):<br/>※ HGLRC wglCreateContext(HDC hdc)  //设置完像素格式后调用,根据DC创建一个RC<br/>※BOOL wglMakeCurrent(HDC hdc, HGLRC hglrc) //关联DC和RC,让RC成为现行(激活)的<br/>※HGLRC wglGetCurrentContext() //返回现行RC<br/>※HDC wglGetCurrentDC() //返回与现行RC关联的那个DC<br/>※BOOL wglDeleteContext(HGLRC hglrc) //删除一个RC(最好先让它变成非现行状态)<br/>※BOOL wglUseFontBitmaps(HDC hdc, DWORD dwFirst, DWORD dwCount, DWORD dwBase) //这个用来根据DC创建字符显示列表<br/><br/>//下面需要用到最重要的第1,2个,至于第5个(删除)在后面退出时用:<br/>		if ( !( m_hRC = wglCreateContext ( m_hDC ) ) ) {.......}<br/>		if ( !wglMakeCurrent ( m_hDC, m_hRC ) ) {.......}<br/><br/>		if ( !InitGL () ) {.......}  //最后还要处理opengl的初始化<br/><br/>	m_bAppIsActive = TRUE;//以上全成功了,可以渲染了!<br/>	}<br/><br/>	return bRet; //没错的话,现在可以打开窗口了<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>然后看看由析构函数调用的KillGLWindow()<br /><br/>&nbsp;</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>GLvoid&nbsp;CMainFrame::KillGLWindow(GLvoid) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span class="comment">//当前是全屏的话,首先检查一下未关闭前的这个屏幕设置能不能直接用到关闭后正常的那个屏幕上,可以的话就直接RESRT成那样就可以了; </span></li>    <li><span class="comment">//不行的话将当前设置变空,再把当初打开窗口前放进m_DMsaved(还记得么)的设置拿出来还给屏幕 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(m_bFullScreen)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(!ChangeDisplaySettings(NULL,CDS_TEST))&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChangeDisplaySettings(NULL,CDS_RESET);&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChangeDisplaySettings(&amp;m_DMsaved,CDS_RESET); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">else</span><span>&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChangeDisplaySettings(NULL,CDS_RESET); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShowCursor(TRUE); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span class="comment">//在有现行RC的情况下,关闭窗口的时候要delete它(delete前最好让它&quot;非现行化&quot;)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span class="keyword">if</span><span>&nbsp;(&nbsp;m_hRC&nbsp;)&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!wglMakeCurrent&nbsp;(&nbsp;NULL,&nbsp;NULL&nbsp;)&nbsp;){....;}</span><span class="comment">//省略的是出错提醒 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!wglDeleteContext&nbsp;(&nbsp;m_hRC&nbsp;)&nbsp;)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;.....;m_hRC&nbsp;=&nbsp;NULL;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span class="comment">//最后删除DC,删除窗口 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;m_hDC&nbsp;&amp;&amp;&nbsp;!::ReleaseDC&nbsp;(&nbsp;m_hWnd,&nbsp;m_hDC&nbsp;)&nbsp;)&nbsp;{....;m_hDC&nbsp;=&nbsp;NULL;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;m_hWnd&nbsp;&amp;&amp;&nbsp;!::DestroyWindow&nbsp;(&nbsp;m_hWnd&nbsp;)&nbsp;){....;m_hDC&nbsp;=&nbsp;NULL;}&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>GLvoid CMainFrame::KillGLWindow(GLvoid)<br/>{<br/>//当前是全屏的话,首先检查一下未关闭前的这个屏幕设置能不能直接用到关闭后正常的那个屏幕上,可以的话就直接RESRT成那样就可以了;<br/>//不行的话将当前设置变空,再把当初打开窗口前放进m_DMsaved(还记得么)的设置拿出来还给屏幕<br/>	if (m_bFullScreen)		<br/>	{<br/>		if (!ChangeDisplaySettings(NULL,CDS_TEST)) {<br/>			ChangeDisplaySettings(NULL,CDS_RESET);	<br/>			ChangeDisplaySettings(&amp;m_DMsaved,CDS_RESET);<br/>		} else {<br/>			ChangeDisplaySettings(NULL,CDS_RESET);<br/>		}			<br/>		ShowCursor(TRUE);<br/>	}<br/>//在有现行RC的情况下,关闭窗口的时候要delete它(delete前最好让它&quot;非现行化&quot;)			<br/>if ( m_hRC ) {<br/>	if ( !wglMakeCurrent ( NULL, NULL ) ){....;}//省略的是出错提醒<br/>	if ( !wglDeleteContext ( m_hRC ) ) {	.....;m_hRC = NULL;}		<br/>	}<br/>//最后删除DC,删除窗口<br/>	if ( m_hDC &amp;&amp; !::ReleaseDC ( m_hWnd, m_hDC ) ) {....;m_hDC = NULL;}<br/>	if ( m_hWnd &amp;&amp; !::DestroyWindow ( m_hWnd ) ){....;m_hDC = NULL;}	<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>最后来粗略看看我说过的NEHE OpenGL框架中与渲染最相关的函数RenderGLScene,ReSizeGLScene,InitGL,因为这些都已经脱离MFC那套,完全是属于OPENGL的东西了。所以详细讲解这里不说了，有兴趣可看看我的OPENGL类别的关于它们的文章。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;CMainFrame::RenderGLScene() </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span class="keyword">if</span><span>&nbsp;(!m_bAppIsActive)</span><span class="keyword">return</span><span>;</span><span class="comment">//记得吗，m_bAppIsActive为真时才渲染 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>glClear(GL_COLOR_BUFFER_BIT&nbsp;|&nbsp;GL_DEPTH_BUFFER_BIT);</span><span class="comment">//清除背静&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Clear&nbsp;Screen&nbsp;And&nbsp;Depth&nbsp;Buffer </span></li>    <li><span>glLoadIdentity();</span><span class="comment">//单位化 </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;........................ </span></li>    <li class="alt"><span>SwapBuffers(m_hDC);</span><span class="comment">//更新缓存区 </span></li>    <li><span>Invalidate(FALSE);&nbsp;&nbsp;</span><span class="comment">//拒绝无效化 </span></li>    <li class="alt"><span>} </span></li>    <li><span class="comment">//初始化和重置窗口大小，要使用时相关代码移到RenderGLScene开始部分 </span></li>    <li class="alt"><span>GLvoid&nbsp;CMainFrame::ReSizeGLScene(GLsizei&nbsp;width,&nbsp;GLsizei&nbsp;height){......} </span></li>    <li><span class="comment">//初始化程序，作OPENGL的预处理 </span></li>    <li class="alt"><span class="keyword">int</span><span>&nbsp;CMainFrame::InitGL(GLvoid){.....}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void CMainFrame::RenderGLScene()<br/>{<br/>if (!m_bAppIsActive)return;//记得吗，m_bAppIsActive为真时才渲染<br/>		<br/>glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除背静			// Clear Screen And Depth Buffer<br/>glLoadIdentity();//单位化<br/><br/>                      ........................<br/>SwapBuffers(m_hDC);//更新缓存区<br/>Invalidate(FALSE);  //拒绝无效化<br/>}<br/>//初始化和重置窗口大小，要使用时相关代码移到RenderGLScene开始部分<br/>GLvoid CMainFrame::ReSizeGLScene(GLsizei width, GLsizei height){......}<br/>//初始化程序，作OPENGL的预处理<br/>int CMainFrame::InitGL(GLvoid){.....}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>&nbsp;终于来到这里，这两天基本都在搞这个啦，一连三篇。辛苦啦，<a href="http://www.zwqxin.com/">ZwqXin</a>！首先感谢MSDN（网络英文版），其次感谢CSDN讨论区的资料，还有数不请BLOG的作者们呵呵，感谢&ldquo;居然能看这种文章&rdquo;然后看到这里的各位大大（我的理解若有问题请不吝指出哦！）。<br /><br/>&nbsp;</p><p>&nbsp;</p>]]></description><category>VC/MFC</category><comments>http://www.zwqxin.com/archives/MFC/3-nehe-frame-vc6.html.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=8</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=8&amp;key=ad975a7b</trackback:ping></item><item><title>自剖一下自己用的NEHE OpenGL框架(中篇)</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html</link><pubDate>Thu, 22 Jan 2009 14:39:08 +0800</pubDate><guid>http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html</guid><description><![CDATA[<p>接着上一篇日志,再接再厉~~本篇是:自剖一下自己用的NEHE OpenGL框架(中篇), 更好地了解NEHE OpenGL框架，同时学习一下MFC咯，自我修炼。（友情提醒:如果你只为OPENGL初学者之初学者,而不懂MFC也不想懂MFC,建议碰到类似的文章先别看[包括本文],更有益的是先<a target="_blank" href="http://www.zwqxin.com/archives/opengl/learn-opengl-first.html">关注渲染部分</a>^^)<br /><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上篇日志请看：<a target="_blank" href="http://www.zwqxin.com/archives/opengl/nehe-frame-vc6.html">自剖一下自己用的NEHE OpenGL框架(上篇)</a></p><p>此日志自我学习用。</p><p>还记得向导生成了一个 CMainframe类和一个C***App类。上篇日志讲了C***App类，现在来看看重要的CMainframe类。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">class</span><span>&nbsp;CMainFrame&nbsp;:&nbsp;</span><span class="keyword">public</span><span>&nbsp;CFrameWnd </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;DECLARE_DYNAMIC(CMainFrame)</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class CMainFrame : public CFrameWnd<br/>{<br/>	DECLARE_DYNAMIC(CMainFrame)</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><div>这里有一个DECLARE_DYNAMIC宏，把它展开，有</div><div>#define DECLARE_DYNAMIC(class_name) \<br /><br/>protected: \<br /><br/>&nbsp;static CRuntimeClass* PASCAL _GetBaseClass(); \<br /><br/>public: \<br /><br/>&nbsp;static const AFX_DATA CRuntimeClass class##class_name; \<br /><br/>&nbsp;virtual CRuntimeClass* GetRuntimeClass() const; \ //</div><div>&nbsp;</div><div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一个（保护函数）明显是为了获得基类。（第二个成员变量）中##是连接符，所以说这里定义了一个CRuntimeClass类型的静态变量classCMainFrame。第三个（虚成员函数）返回这个class##class_name类名。</div><div>&nbsp;</div><div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另外既然名中有类CRuntimeClass，那它必然与运行时识别RTTI有关。好，我先来看看什么是CRuntimeClass：&ldquo;每个从CObject中派生的类都有有一个CRuntimeClass对象同它关联以完成在运行时得到类实例的信息或者是它的基类。&rdquo;因此按我的理解， DECLARE_DYNAMIC的作用主要是获得当前这个类（CMainFrame）的信息，包括基类，并交给CRuntimeClass类实现对它的动态创建，管理等等（RTTI）。如果有错请各看官指出恩。</div><div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 得到这些信息就能实现吗？其实还需要其兄弟IMPLEMENT_DYNAMIC（放在实现文件中）帮忙。很快我们就能遇到了。<br /><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CMainframe类(头文件部分):</div><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">public</span><span>: </span></span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;CMainFrame();&nbsp;</span><span class="comment">//构造函数 </span></li>    <li class="alt"><span class="comment">//Attributes </span></li>    <li><span class="keyword">protected</span><span>: </span></li>    <li class="alt"><span>&nbsp;</span><span class="comment">//设备描述句柄DC(Device&nbsp;Context)和渲染描述句柄RC(Render&nbsp;Contex)， </span></li>    <li><span class="comment">//它们是OPENGL与操作系统，设备间交互所必不可少的。我将在下篇日志详细介绍 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;HDC&nbsp;m_hDC;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;HGLRC&nbsp;m_hRC; </span></li>    <li class="alt"><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; </span></li>    <li><span class="comment">//客户区长宽，我们平时不是可以手动调节渲染窗口的窗口大小吗？这种调节行为其实就是给程序发消息（还记得上篇日志中的PeekMessage么？）这两个参数会在OnSize()中保存调节后实际窗口的长宽，通过对应的消息响应ON_WM_SIZE()上交到消息队列，故暂停渲染来响应PeekMessage处理。 </span></li>    <li class="alt"><span class="comment">//所以平时我们调节窗口大小时可以看到渲染进程是暂停了的。 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;UINT&nbsp;m_cxClient; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;UINT&nbsp;m_cyClient; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//调色板？貌似GDI+上见过类似类型名称的，不知道在这里用来干什么。搜索代码也只见在这里定义和初始化出现，实在迷糊。 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;HPALETTE&nbsp;m_hPal; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//这个是我们屏幕大小&nbsp;1024*768&nbsp;起码最大化的时候要知道吧. </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;VB_WIDTH; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;VB_HEIGHT; </span></li>    <li class="alt"><span class="comment">//绘图深度 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;VB_DEPTH; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//DEVMODE的数据结构描述了要设定的显示器的各类属性值，刷新率啦分辨率啦 </span></li>    <li class="alt"><span class="comment">//OpenGL作图过程中有需要先保存起渲染前的这些屏幕数据，渲染完后返还 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;DEVMODE&nbsp;m_DMsaved; </span></li>    <li class="alt"><span class="comment">//是否全屏幕&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;BOOL&nbsp;m_bFullScreen; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//&nbsp;Operations </span></li>    <li class="alt"><span class="keyword">public</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;RenderGLScene();</span><span class="comment">//渲染函数 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;BOOL&nbsp;m_bAppIsActive;</span><span class="comment">//上篇日志提过，控制是否渲染 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;GLvoid&nbsp;ReSizeGLScene(GLsizei&nbsp;width,&nbsp;GLsizei&nbsp;height);</span><span class="comment">//重置窗口大小 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;InitGL(GLvoid);&nbsp;</span><span class="comment">//初始化 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;GLvoid&nbsp;KillGLWindow(GLvoid);</span><span class="comment">//关闭程序窗口 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;calculateFramesPerSec&nbsp;();</span><span class="comment">//计算FPS，非必要 </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//&nbsp;重载来自父类的两个函数： </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;BOOL&nbsp;PreCreateWindow(CREATESTRUCT&amp;&nbsp;cs);&nbsp;</span><span class="comment">//初始化屏幕 </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//初始化像素设置，绘图设备（上面提到的DC，RC）等等 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;BOOL&nbsp;OnCreateClient(LPCREATESTRUCT&nbsp;lpcs,&nbsp;CCreateContext*&nbsp;pContext); </span></li>    <li><span class="keyword">public</span><span>: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;~CMainFrame();</span><span class="comment">//析构 </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//同样重载（其实是调用父类的），用于DEBUG的函数 </span></li>    <li><span>#ifdef&nbsp;_DEBUG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;</span><span class="comment">//AssertValid提供对对象内部状态的运行时检查，可用于验证成员函数有效值 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;AssertValid()&nbsp;</span><span class="keyword">const</span><span>; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//Dump函数将对象的成员变量的文本化表示形式写入转储上下文(CDumpContext，类似I/O流) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;Dump(CDumpContext&amp;&nbsp;dc)&nbsp;</span><span class="keyword">const</span><span>; </span></li>    <li><span>#endif </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">protected</span><span>: </span></li>    <li class="alt"><span class="comment">//{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;又来了，消息映射表（MESSAGE_MAP，告诉MFC你要处理什么消息，在哪里），不过这次是&ldquo;真&rdquo;的了。 </span></li>    <li><span>afx_msg&nbsp;</span><span class="keyword">void</span><span>&nbsp;OnPaint();</span><span class="comment">//对应消息ON_WM_PAINT()（以下类似）告诉窗口绘制自己 </span></li>    <li class="alt"><span>afx_msg&nbsp;</span><span class="keyword">void</span><span>&nbsp;OnSize(UINT&nbsp;nType,&nbsp;</span><span class="keyword">int</span><span>&nbsp;cx,&nbsp;</span><span class="keyword">int</span><span>&nbsp;cy);</span><span class="comment">//告诉窗口改变大小 </span></li>    <li><span>afx_msg&nbsp;</span><span class="keyword">void</span><span>&nbsp;OnSetFocus(CWnd&nbsp;*pOldWnd);</span><span class="comment">//告诉窗口设立焦点 </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//当某一窗口即将激活时，主框架窗口将收到WM_QUERYNEWPALETTE消息，通知该窗口将要收到输入焦点，给它一次机会实现其自身的逻辑调色板；&nbsp; </span></li>    <li class="alt"><span class="comment">//当系统调色板改变后，主框架窗口将收到WM_PALETTECHANGED消息，通知其它窗口系统调色板已经改变，此时每一窗口都应该实现其逻辑调色板，重画客户区。 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;afx_msg&nbsp;BOOL&nbsp;OnQueryNewPalette(); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;afx_msg&nbsp;</span><span class="keyword">void</span><span>&nbsp;OnPaletteChanged(CWnd*&nbsp;pFocusWnd);</span><span class="comment">//告诉窗口绘制自己 </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//窗口被激活的时候触发，譬如你用另一个应用的窗口掩盖渲染穿口了 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;afx_msg&nbsp;</span><span class="keyword">void</span><span>&nbsp;OnActivateApp(BOOL&nbsp;bActive,&nbsp;HTASK&nbsp;hTask); </span></li>    <li class="alt"><span class="comment">//按键消息 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;afx_msg&nbsp;</span><span class="keyword">void</span><span>&nbsp;OnKeyDown(UINT&nbsp;nChar,&nbsp;UINT&nbsp;nRepCnt,&nbsp;UINT&nbsp;nFlags); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//}}AFX_MSG </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;DECLARE_MESSAGE_MAP()</span><span class="comment">//（通过它程序找到以上对应的消息响应函数） </span></li>    <li class="alt"><span>};</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>public:<br/>	CMainFrame(); //构造函数<br/>//Attributes<br/>protected:<br/> //设备描述句柄DC(Device Context)和渲染描述句柄RC(Render Contex)，<br/>//它们是OPENGL与操作系统，设备间交互所必不可少的。我将在下篇日志详细介绍<br/>	HDC m_hDC;								<br/>	HGLRC m_hRC;<br/>							<br/>//客户区长宽，我们平时不是可以手动调节渲染窗口的窗口大小吗？这种调节行为其实就是给程序发消息（还记得上篇日志中的PeekMessage么？）这两个参数会在OnSize()中保存调节后实际窗口的长宽，通过对应的消息响应ON_WM_SIZE()上交到消息队列，故暂停渲染来响应PeekMessage处理。<br/>//所以平时我们调节窗口大小时可以看到渲染进程是暂停了的。<br/>	UINT m_cxClient;<br/>	UINT m_cyClient;<br/><br/>//调色板？貌似GDI+上见过类似类型名称的，不知道在这里用来干什么。搜索代码也只见在这里定义和初始化出现，实在迷糊。<br/>	HPALETTE m_hPal;<br/><br/>//这个是我们屏幕大小 1024*768 起码最大化的时候要知道吧.<br/>	int VB_WIDTH;<br/>	int VB_HEIGHT;<br/>//绘图深度<br/>	int VB_DEPTH;<br/><br/>//DEVMODE的数据结构描述了要设定的显示器的各类属性值，刷新率啦分辨率啦<br/>//OpenGL作图过程中有需要先保存起渲染前的这些屏幕数据，渲染完后返还<br/>	DEVMODE	m_DMsaved;<br/>//是否全屏幕				      <br/>	BOOL m_bFullScreen;<br/><br/>// Operations<br/>public:<br/>	void RenderGLScene();//渲染函数<br/>	BOOL m_bAppIsActive;//上篇日志提过，控制是否渲染<br/>	GLvoid ReSizeGLScene(GLsizei width, GLsizei height);//重置窗口大小<br/>	int InitGL(GLvoid);	//初始化<br/>	GLvoid KillGLWindow(GLvoid);//关闭程序窗口<br/>	void calculateFramesPerSec ();//计算FPS，非必要<br/><br/>// 重载来自父类的两个函数：<br/>	virtual BOOL PreCreateWindow(CREATESTRUCT&amp; cs); //初始化屏幕<br/><br/>//初始化像素设置，绘图设备（上面提到的DC，RC）等等<br/>	virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);<br/>public:<br/>	virtual ~CMainFrame();//析构<br/><br/>//同样重载（其实是调用父类的），用于DEBUG的函数<br/>#ifdef _DEBUG       <br/> //AssertValid提供对对象内部状态的运行时检查，可用于验证成员函数有效值<br/>	virtual void AssertValid() const;<br/><br/>//Dump函数将对象的成员变量的文本化表示形式写入转储上下文(CDumpContext，类似I/O流)<br/>	virtual void Dump(CDumpContext&amp; dc) const;<br/>#endif<br/><br/>protected:<br/>//{     又来了，消息映射表（MESSAGE_MAP，告诉MFC你要处理什么消息，在哪里），不过这次是&ldquo;真&rdquo;的了。<br/>afx_msg void OnPaint();//对应消息ON_WM_PAINT()（以下类似）告诉窗口绘制自己<br/>afx_msg void OnSize(UINT nType, int cx, int cy);//告诉窗口改变大小<br/>afx_msg void OnSetFocus(CWnd *pOldWnd);//告诉窗口设立焦点<br/><br/>//当某一窗口即将激活时，主框架窗口将收到WM_QUERYNEWPALETTE消息，通知该窗口将要收到输入焦点，给它一次机会实现其自身的逻辑调色板； <br/>//当系统调色板改变后，主框架窗口将收到WM_PALETTECHANGED消息，通知其它窗口系统调色板已经改变，此时每一窗口都应该实现其逻辑调色板，重画客户区。<br/>	afx_msg BOOL OnQueryNewPalette();<br/>	afx_msg void OnPaletteChanged(CWnd* pFocusWnd);//告诉窗口绘制自己<br/><br/>//窗口被激活的时候触发，譬如你用另一个应用的窗口掩盖渲染穿口了<br/>	afx_msg void OnActivateApp(BOOL bActive, HTASK hTask);<br/>//按键消息<br/>	afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);<br/>	//}}AFX_MSG<br/>	DECLARE_MESSAGE_MAP()//（通过它程序找到以上对应的消息响应函数）<br/>};</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>当然了，还可以在编程过程中加入更多事件(譬如鼠标行为) vc6中在工作空间右键点击CMainframe类----ADD WINDOWS HANDLER,下面再看看实现中开头那段代码：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>IMPLEMENT_DYNAMIC(CMainFrame,&nbsp;CFrameWnd) </span></span></li>    <li><span class="comment">//IMPLEMENT_DYNAMIC（参数为本类,本类的父类）与上面的DECLARE_DYNAMIC是两兄弟，不过它位于实现文件，打开： </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//#define&nbsp;IMPLEMENT_DYNAMIC(class_name,&nbsp;base_class_name)&nbsp;\ </span></li>    <li class="alt"><span class="comment">//&nbsp;&nbsp;&nbsp;&nbsp;IMPLEMENT_RUNTIMECLASS(class_name,&nbsp;base_class_name,&nbsp;0xFFFF,&nbsp;NULL) </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//就不再继续打开了，简单说明其作用：通过运行时在串行结构中为动态CObject派生类访问类名和位置来产生必要的C++代码（RTTI）。这就用到DECLARE_DYNAMIC获得的东西了 </span></li>    <li><span class="comment">//其实一言毙之：就是在程序里搞RTTI的，MFC加了它们俩，没错的~（误） </span></li>    <li class="alt">&nbsp;</li>    <li><span>BEGIN_MESSAGE_MAP(CMainFrame,&nbsp;CFrameWnd)&nbsp;</span><span class="comment">//开始消息影射 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//{{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//这些就是窗口消息。与上面消息响应处理函数一一对应 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;ON_WM_PAINT() </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ON_WM_SIZE() </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;ON_WM_SETFOCUS() </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ON_WM_QUERYNEWPALETTE() </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;ON_WM_PALETTECHANGED() </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ON_WM_ACTIVATEAPP() </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;ON_WM_KEYDOWN() </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//}} </span></li>    <li><span>END_MESSAGE_MAP()</span><span class="comment">//结束消息映射 </span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)<br/>//IMPLEMENT_DYNAMIC（参数为本类,本类的父类）与上面的DECLARE_DYNAMIC是两兄弟，不过它位于实现文件，打开：<br/><br/>//#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \<br/>//    IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)<br/><br/>//就不再继续打开了，简单说明其作用：通过运行时在串行结构中为动态CObject派生类访问类名和位置来产生必要的C++代码（RTTI）。这就用到DECLARE_DYNAMIC获得的东西了<br/>//其实一言毙之：就是在程序里搞RTTI的，MFC加了它们俩，没错的~（误）<br/><br/>BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //开始消息影射<br/>	//{{       //这些就是窗口消息。与上面消息响应处理函数一一对应<br/>	ON_WM_PAINT()<br/>	ON_WM_SIZE()<br/>	ON_WM_SETFOCUS()<br/>	ON_WM_QUERYNEWPALETTE()<br/>	ON_WM_PALETTECHANGED()<br/>	ON_WM_ACTIVATEAPP()<br/>	ON_WM_KEYDOWN()<br/>	//}}<br/>END_MESSAGE_MAP()//结束消息映射<br/></pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>现在我们先跳过实现的其他部分(放在下篇介绍),看看实现里面相应的消息处理函数:</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="comment">//前面带空洪afx_msg的消息处理函数基本其实也是来自CWnd的,只是没有virtual符号而已(我不知道这算重写不,还是掩盖了,以后了解多点MFC再说). </span></span></li>    <li><span class="keyword">void</span><span>&nbsp;CMainFrame::OnPaint()</span><span class="comment">//告诉窗口重绘自己 </span></li>    <li class="alt"><span>{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;CPaintDC&nbsp;dc(</span><span class="keyword">this</span><span>);&nbsp;&nbsp;&nbsp;</span><span class="comment">//得到一个绘图用的DC </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;加自己的东西(其实我都没加过&nbsp;-&nbsp;-)&nbsp;&nbsp; </span></li>    <li><span class="comment">//以下是CWnd的函数,用处是使窗口有效,并&nbsp;清除消息队列中的WM_PAINT消息 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;::ValidateRect&nbsp;(&nbsp;m_hWnd,&nbsp;NULL&nbsp;); </span></li>    <li><span>} </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CMainFrame::OnSize(UINT,&nbsp;</span><span class="keyword">int</span><span>&nbsp;cx,&nbsp;</span><span class="keyword">int</span><span>&nbsp;cy) </span></li>    <li><span>{&nbsp;&nbsp;&nbsp;</span><span class="comment">//重新调整客户区域(窗口大小)时启动,ReSizeGLScene就是调整函数(<a target="_blank" href="http://www.zwqxin.com/archives/MFC/3-nehe-frame-vc6.html.html">见下篇</a>) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;m_cxClient&nbsp;=&nbsp;cx; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;m_cyClient&nbsp;=&nbsp;cy; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ReSizeGLScene(&nbsp;cx,&nbsp;cy&nbsp;); </span></li>    <li><span>} </span></li>    <li class="alt"><span class="comment">//其实下面两个处理函数功用<a href="#ftg">上面</a>着重说过了,这里说说实现的问题 </span></li>    <li><span>BOOL&nbsp;CMainFrame::OnQueryNewPalette() </span></li>    <li class="alt"><span>{&nbsp;</span><span class="comment">//判断窗口是否即将要重绘(其实根本没判断,直接TRUE并重绘了&nbsp;-&nbsp;-) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Invalidate(); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;TRUE; </span></li>    <li><span>} </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CMainFrame::OnPaletteChanged(CWnd*&nbsp;pFocusWnd) </span></li>    <li><span>{&nbsp;</span><span class="comment">//注意只对主(顶)框架(top-level&nbsp;and&nbsp;overlapped&nbsp;windows)有效&nbsp;&nbsp; </span></li>    <li class="alt"><span class="keyword">if</span><span>&nbsp;((pFocusWnd&nbsp;!=&nbsp;</span><span class="keyword">this</span><span>)&nbsp;&amp;&amp;&nbsp;(!IsChild(pFocusWnd))) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnQueryNewPalette();&nbsp;</span><span class="comment">//重绘 </span></li>    <li class="alt"><span>} </span></li>    <li><span class="keyword">void</span><span>&nbsp;CMainFrame::OnSetFocus(CWnd*&nbsp;pOldWnd) </span></li>    <li class="alt"><span>{&nbsp;&nbsp;</span><span class="comment">//窗口即将获得焦点时,重绘 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;OnQueryNewPalette(); </span></li>    <li class="alt"><span>} </span></li>    <li><span class="keyword">void</span><span>&nbsp;CMainFrame::OnActivateApp(BOOL&nbsp;bActive,&nbsp;HTASK&nbsp;hTask) </span></li>    <li class="alt"><span>{</span><span class="comment">//调用上层OnActivateApp,bActive判断遮掩情况,然后赋予本地的判断变量m_bAppIsActive,m_bAppIsActive控制是否渲染 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;CFrameWnd::OnActivateApp(bActive,&nbsp;hTask); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;m_bAppIsActive&nbsp;=&nbsp;bActive; </span></li>    <li><span>} </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CMainFrame::OnKeyDown(UINT&nbsp;nChar,&nbsp;UINT&nbsp;nRepCnt,&nbsp;UINT&nbsp;nFlags)&nbsp; </span></li>    <li><span>{</span><span class="comment">//同样调用上层函数,这里用switch控制按键 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CFrameWnd::OnKeyDown(nChar,&nbsp;nRepCnt,&nbsp;nFlags); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">switch</span><span>&nbsp;(&nbsp;nChar&nbsp;)&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">case</span><span>&nbsp;VK_ESCAPE:&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PostMessage&nbsp;(&nbsp;WM_CLOSE&nbsp;); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">break</span><span>; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//前面带空洪afx_msg的消息处理函数基本其实也是来自CWnd的,只是没有virtual符号而已(我不知道这算重写不,还是掩盖了,以后了解多点MFC再说).<br/>void CMainFrame::OnPaint()//告诉窗口重绘自己<br/>{<br/>	CPaintDC dc(this);   //得到一个绘图用的DC<br/>	// 加自己的东西(其实我都没加过 - -)	<br/>//以下是CWnd的函数,用处是使窗口有效,并 清除消息队列中的WM_PAINT消息<br/>	::ValidateRect ( m_hWnd, NULL );<br/>}<br/>void CMainFrame::OnSize(UINT, int cx, int cy)<br/>{   //重新调整客户区域(窗口大小)时启动,ReSizeGLScene就是调整函数(见下篇)<br/>	m_cxClient = cx;<br/>	m_cyClient = cy;<br/>	ReSizeGLScene( cx, cy );<br/>}<br/>//其实下面两个处理函数功用上面着重说过了,这里说说实现的问题<br/>BOOL CMainFrame::OnQueryNewPalette()<br/>{ //判断窗口是否即将要重绘(其实根本没判断,直接TRUE并重绘了 - -)<br/>	Invalidate();<br/>	return TRUE;<br/>}<br/>void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd)<br/>{ //注意只对主(顶)框架(top-level and overlapped windows)有效	<br/>if ((pFocusWnd != this) &amp;&amp; (!IsChild(pFocusWnd)))<br/>		OnQueryNewPalette(); //重绘<br/>}<br/>void CMainFrame::OnSetFocus(CWnd* pOldWnd)<br/>{  //窗口即将获得焦点时,重绘<br/>	OnQueryNewPalette();<br/>}<br/>void CMainFrame::OnActivateApp(BOOL bActive, HTASK hTask)<br/>{//调用上层OnActivateApp,bActive判断遮掩情况,然后赋予本地的判断变量m_bAppIsActive,m_bAppIsActive控制是否渲染<br/>	CFrameWnd::OnActivateApp(bActive, hTask);<br/>	m_bAppIsActive = bActive;<br/>}<br/>void CMainFrame::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) <br/>{//同样调用上层函数,这里用switch控制按键<br/>     CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags);<br/>	switch ( nChar ) {<br/>	case VK_ESCAPE:	<br/>		PostMessage ( WM_CLOSE );<br/>		break;<br/>	} 	<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>不知不绝就那么长了，接下来还有其他实现部分，惟有多开一篇日志来剖了~~<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/MFC/3-nehe-frame-vc6.html.html">自剖一下自己用的NEHE OpenGL框架(下篇)&nbsp;</a></p>]]></description><category>VC/MFC</category><comments>http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=7</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=7&amp;key=81f05381</trackback:ping></item><item><title>自剖一下自己用的NEHE OpenGL框架(上篇)</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/MFC/nehe-frame-vc6.html</link><pubDate>Wed, 21 Jan 2009 21:28:37 +0800</pubDate><guid>http://www.zwqxin.com/archives/MFC/nehe-frame-vc6.html</guid><description><![CDATA[<p>话说学OpenGL也有一些时间了，但是其实感觉自己对很多东西还是一知半懂。在大战开始前夕（考试那段日子被shadow volume原理弄得半崩溃状，曾下决心寒假初一定要自己呕个实现出来~），我得先捡回一些&ldquo;一知半懂&rdquo;的知识。这里作为自我修炼的一环，目的在于回头看看自己一直在用的NEHE框架。(友情提醒:如果你只为OPENGL初学者之初学者,而不懂MFC也不想懂MFC,建议碰到类似的文章先别看[包括本文],更有益的是<a target="_blank" href="http://www.zwqxin.com/archives/opengl/learn-opengl-first.html">关注渲染部分</a>^^)</p><p>此日志自我学习用。</p><p>向导生成了一个 CMainframe类和一个C***App类。先来看看C***App类。<br /><br/>C***App类(头文件部分):</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="preprocessor">#if&nbsp;_MSC_VER&nbsp;&gt;&nbsp;1000&nbsp;&nbsp;&nbsp;&nbsp;//如果vc编译器的版本大于1000则...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></li>    <li><span>#pragma&nbsp;once </span></li>    <li class="alt"><span>#endif&nbsp;//&nbsp;_MSC_VER&nbsp;&gt;&nbsp;1000 </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//关于#pragma&nbsp;once和#ifndef-#define-#endif的区别： </span></li>    <li><span class="comment">//1.前者指由编译器保证&ldquo;某一个文件&rdquo;（the&nbsp;same&nbsp;file）不会被包含多次 </span></li>    <li class="alt"><span class="comment">//（但是如果有多个&quot;同名同内容的头文件&quot;，那么它们就造成了重复包含，这时候应该用后者来避免）； </span></li>    <li><span class="comment">//2.后者指编译器保证&quot;同一个名称的文件&quot;(files&nbsp;with&nbsp;the&nbsp;same&nbsp;name)不会被包含多次 </span></li>    <li class="alt"><span class="comment">//（但是因为它是按名称来标识文件的，所以如果有多个&ldquo;同名而不同内容的头文件&ldquo;，其中一个就会被掩盖了）。 </span></li>    <li><span class="comment">//所以，如果有两个同名的.h,(而不想或无法或忘记改名的时候,)如果内容不同,#pragma&nbsp;once;&nbsp;相同,#ifndef </span></li>    <li class="alt">&nbsp;</li>    <li><span>#ifndef&nbsp;__AFXWIN_H__ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;#error&nbsp;include&nbsp;'stdafx.h'&nbsp;before&nbsp;including&nbsp;this&nbsp;file&nbsp;for&nbsp;PCH </span></li>    <li><span>#endif </span></li>    <li class="alt"><span class="comment">//这是要求所有源代码模块都包含预编译头文件（这样能够加快编译的速度），如果没包含就报错。 </span></li>    <li>&nbsp;</li>    <li class="alt"><span>#include&nbsp;&quot;resource.h&quot; </span></li>    <li><span>&nbsp;</span><span class="comment">//资源文件和源代码文件的桥梁,伙同资源文件.rc,链成.res</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>#if _MSC_VER &gt; 1000    //如果vc编译器的版本大于1000则...     <br/>#pragma once<br/>#endif // _MSC_VER &gt; 1000<br/><br/>//关于#pragma once和#ifndef-#define-#endif的区别：<br/>//1.前者指由编译器保证&ldquo;某一个文件&rdquo;（the same file）不会被包含多次<br/>//（但是如果有多个&quot;同名同内容的头文件&quot;，那么它们就造成了重复包含，这时候应该用后者来避免）；<br/>//2.后者指编译器保证&quot;同一个名称的文件&quot;(files with the same name)不会被包含多次<br/>//（但是因为它是按名称来标识文件的，所以如果有多个&ldquo;同名而不同内容的头文件&ldquo;，其中一个就会被掩盖了）。<br/>//所以，如果有两个同名的.h,(而不想或无法或忘记改名的时候,)如果内容不同,#pragma once; 相同,#ifndef<br/><br/>#ifndef __AFXWIN_H__<br/>	#error include 'stdafx.h' before including this file for PCH<br/>#endif<br/>//这是要求所有源代码模块都包含预编译头文件（这样能够加快编译的速度），如果没包含就报错。<br/><br/>#include &quot;resource.h&quot;<br/> //资源文件和源代码文件的桥梁,伙同资源文件.rc,链成.res</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">class</span><span>&nbsp;C***App&nbsp;:&nbsp;</span><span class="keyword">public</span><span>&nbsp;CWinApp&nbsp;&nbsp;</span><span class="comment">//继承CWinApp,&nbsp;控制整个应用程序 </span></span></li>    <li><span>{&nbsp;&nbsp;&nbsp;</span><span class="comment">//运行程序要将该类实例化，并调用构造和重载的InitInstance() </span></li>    <li class="alt"><span>&nbsp;&nbsp;</span><span class="comment">//因此事实上是透过CWinApp进入WinMain,WinMain对其InitInstance() </span></li>    <li><span>&nbsp;&nbsp;</span><span class="comment">//的控制导致C***App&nbsp;::InitInstance()的自动执行,开始了一个应用程序实例的进程 </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">public</span><span>:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;C***App(); </span></li>    <li class="alt"><span class="keyword">private</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;LONGLONG&nbsp;&nbsp;&nbsp;m_lCurTime;&nbsp;&nbsp;&nbsp;</span><span class="comment">//当前时间&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_dwTimeCount;&nbsp;</span><span class="comment">//每帧的用时(没counter的时候就拿它作默认值) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;LONGLONG&nbsp;&nbsp;&nbsp;m_lPerfCounter;&nbsp;</span><span class="comment">//&nbsp;定时器频率 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;BOOL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_bPerFlag;&nbsp;</span><span class="comment">//&nbsp;选择器,决定哪个定时器起作用 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;LONGLONG&nbsp;&nbsp;&nbsp;m_lNextTime;</span><span class="comment">//&nbsp;渲染下一帧的时间 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;LONGLONG&nbsp;&nbsp;&nbsp;m_lLastTime;&nbsp;</span><span class="comment">//&nbsp;当前帧(开始)的时间 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_dTimeElapsed;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;自从当前帧开始到现在的时间 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_dTimeScale;&nbsp;&nbsp;</span><span class="comment">//&nbsp;一个时间的乘数 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;LONGLONG&nbsp;&nbsp;&nbsp;&nbsp;m_lFramesPerSecond;&nbsp;</span><span class="comment">//&nbsp;程序运行时的FPS </span></li>    <li class="alt"><span class="comment">//&nbsp;重载的函数 </span></li>    <li><span class="keyword">public</span><span>: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;BOOL&nbsp;InitInstance(); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;Run();&nbsp;&nbsp;&nbsp;</span><span class="comment">//这个很明显....run </span></li>    <li class="alt"><span class="keyword">public</span><span>:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//这里留给编译器,谁也别动&nbsp;(DO&nbsp;NOT&nbsp;EDIT&nbsp;HERE)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;DECLARE_MESSAGE_MAP()&nbsp;</span><span class="comment">//消息映射函数,关于它可可以延伸一下: </span></li>    <li><span>}; </span></li>    <li class="alt"><span class="comment">//MFC中提供了四个宏:&nbsp; </span></li>    <li><span class="comment">//DECLARE_MESSAGE_MAP()&nbsp;:声明一些函数供接下来使用 </span></li>    <li class="alt"><span class="comment">//BEGIN_MESSAGE_MAP(子类,父类)&nbsp;:实现开始(&nbsp;把消息函数的函数指针联系起来) </span></li>    <li><span class="comment">//ON_COMMAND&nbsp;&nbsp;&nbsp;&nbsp;:定义具体的消息和消息对应的函数 </span></li>    <li class="alt"><span class="comment">//END_MESSAGE_MAP()&nbsp;:&nbsp;实现结束.&nbsp;&nbsp;你看<a target="_blank" href="http://www.zwqxin.com/archives/opengl/2-nehe-frame-vc6.html">下一篇接下来的实现</a>就明白啦　　&nbsp;&nbsp; </span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class C***App : public CWinApp  //继承CWinApp, 控制整个应用程序<br/>{   //运行程序要将该类实例化，并调用构造和重载的InitInstance()<br/>  //因此事实上是透过CWinApp进入WinMain,WinMain对其InitInstance()<br/>  //的控制导致C***App ::InitInstance()的自动执行,开始了一个应用程序实例的进程<br/><br/>public:       C***App();<br/>private:<br/>	LONGLONG   m_lCurTime;   //当前时间	<br/>	DWORD	    m_dwTimeCount; //每帧的用时(没counter的时候就拿它作默认值)<br/>	LONGLONG   m_lPerfCounter; // 定时器频率<br/>	BOOL	    m_bPerFlag; // 选择器,决定哪个定时器起作用<br/>	LONGLONG   m_lNextTime;// 渲染下一帧的时间<br/>	LONGLONG   m_lLastTime;	// 当前帧(开始)的时间<br/>	double	     m_dTimeElapsed;	// 自从当前帧开始到现在的时间<br/>	double	     m_dTimeScale;	// 一个时间的乘数<br/>	LONGLONG    m_lFramesPerSecond; // 程序运行时的FPS<br/>// 重载的函数<br/>public:<br/>	virtual BOOL InitInstance();<br/>	virtual int Run();   //这个很明显....run<br/>public:         <br/>    //这里留给编译器,谁也别动 (DO NOT EDIT HERE) 		       <br/>	DECLARE_MESSAGE_MAP() //消息映射函数,关于它可可以延伸一下:<br/>};<br/>//MFC中提供了四个宏: <br/>//DECLARE_MESSAGE_MAP() :声明一些函数供接下来使用<br/>//BEGIN_MESSAGE_MAP(子类,父类) :实现开始( 把消息函数的函数指针联系起来)<br/>//ON_COMMAND    :定义具体的消息和消息对应的函数<br/>//END_MESSAGE_MAP() : 实现结束.  你看下一篇接下来的实现就明白啦　　	<br/></pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>然后就来看看实现啦：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="preprocessor">#ifdef&nbsp;&nbsp;&nbsp;_DEBUG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;判断是否定义_DEBUG，如果是，则： </span></span></li>    <li><span>#define&nbsp;&nbsp;new&nbsp;&nbsp;&nbsp;&nbsp;DEBUG_NEW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;定义调试new宏，取代new关键字 </span></li>    <li class="alt"><span class="comment">//原来我们一直在用的new早被替换了呀~阴啊阴啊 </span></li>    <li><span class="comment">//这个DEBUG_NEW可以在DEBUG状态下定位内存泄露并且跟踪文件名 </span></li>    <li class="alt"><span class="comment">//追踪它，发现&nbsp;#define&nbsp;DEBUG_NEW&nbsp;new(THIS_FILE,&nbsp;__LINE__) </span></li>    <li><span class="comment">//晕，这是啥米new啊？既不像placement-new也没看见&lt;new&gt;啊 </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//网上说是new的重载只要满足第一个参数是个SIZE，接下来可以乱（？）搞啦 </span></li>    <li class="alt"><span class="comment">//MFC把这里得到每个new调用的文件名及所在源文件中的行数记录下来， </span></li>    <li><span class="comment">//如此当delete时就可以检查所有原申请的内存， </span></li>    <li class="alt"><span class="comment">//如果&nbsp;发现内存指针没有被释放，呵呵，你就惨了（内存泄露了！！）， </span></li>    <li><span class="comment">//这时编译器会告诉你是哪个new（在哪行）申请的内存没被释放。 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>#undef&nbsp;&nbsp;&nbsp;THIS_FILE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;取消THIS_FILE的宏定义，使得其暂无定义&nbsp;&nbsp; </span></li>    <li class="alt"><span class="keyword">static</span><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">char</span><span>&nbsp;&nbsp;&nbsp;THIS_FILE[]=__FILE__;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;&nbsp;&nbsp;定义THIS_FILE指向文件名&nbsp;&nbsp;&nbsp; </span></li>    <li><span class="comment">//THIS_FILE&nbsp;变量，如你所见，作为一个数组存储__FILE__&nbsp; </span></li>    <li class="alt"><span class="comment">//__FILE__&nbsp;&nbsp;&nbsp;常量，是编译器能识别的预定义ANSI-&nbsp;C&nbsp;6宏之一，武功盖世，代表当前文件名（**.cpp） </span></li>    <li><span>#endif&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;结束&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>#ifdef   _DEBUG     //   判断是否定义_DEBUG，如果是，则：<br/>#define  new    DEBUG_NEW     //   定义调试new宏，取代new关键字<br/>//原来我们一直在用的new早被替换了呀~阴啊阴啊<br/>//这个DEBUG_NEW可以在DEBUG状态下定位内存泄露并且跟踪文件名<br/>//追踪它，发现 #define DEBUG_NEW new(THIS_FILE, __LINE__)<br/>//晕，这是啥米new啊？既不像placement-new也没看见&lt;new&gt;啊<br/><br/>//网上说是new的重载只要满足第一个参数是个SIZE，接下来可以乱（？）搞啦<br/>//MFC把这里得到每个new调用的文件名及所在源文件中的行数记录下来，<br/>//如此当delete时就可以检查所有原申请的内存，<br/>//如果 发现内存指针没有被释放，呵呵，你就惨了（内存泄露了！！），<br/>//这时编译器会告诉你是哪个new（在哪行）申请的内存没被释放。<br/>    <br/>#undef   THIS_FILE     //   取消THIS_FILE的宏定义，使得其暂无定义  <br/>static   char   THIS_FILE[]=__FILE__;     //   定义THIS_FILE指向文件名   <br/>//THIS_FILE 变量，如你所见，作为一个数组存储__FILE__ <br/>//__FILE__   常量，是编译器能识别的预定义ANSI- C 6宏之一，武功盖世，代表当前文件名（**.cpp）<br/>#endif     //   结束      </pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div><br /><br/>&nbsp;</div><p>其实MFC里所有实现文件都这样做，是为什么？呵呵，一个DEBUG除虫的环境啦！</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span>BEGIN_MESSAGE_MAP(CFgfgfApp,&nbsp;CWinApp) </span></span></li>    <li><span class="comment">//..............&nbsp;&nbsp;这啥？没东西？呵呵，都说例子在下篇日志咯，这里COMMAND为空do&nbsp;nothing </span></li>    <li class="alt"><span>END_MESSAGE_MAP() </span></li>    <li>&nbsp;</li>    <li class="alt"><span>C***App::C***App()&nbsp;&nbsp;</span><span class="comment">//构造函数，基本设0，除了： </span></li>    <li><span>{.........}&nbsp;</span><span class="comment">//m_dwTimeCount&nbsp;=&nbsp;8（每帧持续8秒）FPS预设90 </span></li>    <li class="alt">&nbsp;</li>    <li><span>C***App&nbsp;theApp;&nbsp;</span><span class="comment">//实例了啊实例了啊 </span></li>    <li class="alt">&nbsp;</li>    <li><span>BOOL&nbsp;C***App::InitInstance() </span></li>    <li class="alt"><span>{ </span></li>    <li><span class="comment">//公司注册？？汗 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;SetRegistryKey(_T(</span><span class="string">&quot;Local&nbsp;AppWizard-Generated&nbsp;Applications&quot;</span><span>)); </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//接下来才是真的：构筑窗口作为应用程序的主窗口对象： </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;m_pMainWnd&nbsp;=&nbsp;NULL; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;CMainFrame*&nbsp;pFrame&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;CMainFrame;&nbsp;</span><span class="comment">//调用CMainFrame类(框架类)了,<a target="_blank" href="http://www.zwqxin.com/archives/opengl/2-nehe-frame-vc6.html">下篇讲述</a> </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//这个Create()函数是CMainFrame类的父亲CFrameWnd的,然后CFrameWnd有个父亲叫CWnd,CWnd有个父亲叫CCmdTarget,CCmdTarget有个父亲叫CObject,(它别名可能叫亚当之子) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(!pFrame-&gt;Create(NULL,</span><span class="string">&quot;MFC&nbsp;OpenGL&quot;</span><span>)) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;FALSE; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//话说回来这个m_pMainWnd是从哪里弹出来的呢?哦~原来是咱们C***App的父亲CWinApp的父亲CWinThread那继承来的啊.(注意,CWinThread父亲就是上面提到的CCmdTarget.&nbsp;哈都是CObject子孙嘛) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;m_pMainWnd&nbsp;=&nbsp;pFrame;&nbsp;</span><span class="comment">//获得窗口对象 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;pFrame-&gt;ShowWindow(m_nCmdShow);&nbsp;</span><span class="comment">//显示窗口 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;pFrame-&gt;UpdateWindow();</span><span class="comment">//更新,重绘屏幕 </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;TRUE; </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>BEGIN_MESSAGE_MAP(CFgfgfApp, CWinApp)<br/>//..............  这啥？没东西？呵呵，都说例子在下篇日志咯，这里COMMAND为空do nothing<br/>END_MESSAGE_MAP()<br/><br/>C***App::C***App()  //构造函数，基本设0，除了：<br/>{.........} //m_dwTimeCount = 8（每帧持续8秒）FPS预设90<br/><br/>C***App theApp; //实例了啊实例了啊<br/><br/>BOOL C***App::InitInstance()<br/>{<br/>//公司注册？？汗<br/>	SetRegistryKey(_T(&quot;Local AppWizard-Generated Applications&quot;));<br/><br/>//接下来才是真的：构筑窗口作为应用程序的主窗口对象：<br/>	m_pMainWnd = NULL;<br/>	CMainFrame* pFrame = new CMainFrame; //调用CMainFrame类(框架类)了,下篇讲述<br/><br/>//这个Create()函数是CMainFrame类的父亲CFrameWnd的,然后CFrameWnd有个父亲叫CWnd,CWnd有个父亲叫CCmdTarget,CCmdTarget有个父亲叫CObject,(它别名可能叫亚当之子)<br/>	if (!pFrame-&gt;Create(NULL,&quot;MFC OpenGL&quot;))<br/>		return FALSE;<br/><br/>//话说回来这个m_pMainWnd是从哪里弹出来的呢?哦~原来是咱们C***App的父亲CWinApp的父亲CWinThread那继承来的啊.(注意,CWinThread父亲就是上面提到的CCmdTarget. 哈都是CObject子孙嘛)<br/>	m_pMainWnd = pFrame; //获得窗口对象<br/>	pFrame-&gt;ShowWindow(m_nCmdShow); //显示窗口<br/>	pFrame-&gt;UpdateWindow();//更新,重绘屏幕<br/><br/>	return TRUE;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>接下来就是一个带计时器功能,控制窗口怎样重绘法的Run()函数啦,别忘记它也是重载而来的哦.</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">int</span><span>&nbsp;C***App::Run() </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(!m_pMainWnd) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AfxPostQuitMessage(0); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;MSG&nbsp;msg; </span></li>    <li class="alt"><span class="comment">//STATIC_DOWNCAST&nbsp;.....强制类型转换的宏?<a target="_blank" href="http://msdn.microsoft.com/en-us/library/cfy5wsfk(VS.80).aspx">请看MSDN </a></span></li>    <li><span class="comment">//m_pMainWnd&nbsp;被直接转换为指向CMainFrame类(这编译器转型要求此类必须继承于CObject&nbsp;&nbsp;-&nbsp;-)的指针了 </span></li>    <li class="alt"><span>CMainFrame*&nbsp;pFrame&nbsp;=&nbsp;STATIC_DOWNCAST&nbsp;(&nbsp;CMainFrame,&nbsp;m_pMainWnd&nbsp;); </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//QueryPerformanceFrequency:高精计频器获取一个指向定时器每秒的频率数(m_lPerfCounter&nbsp;,单位n/s)的指针,并把系统值(CPU时钟频率,每秒嘀哒声的个数)交给它.返回值表明是否支持高精计时 </span></li>    <li><span class="comment">//LARGE_INTEGER,结构体类型,模拟64位有符号的二进制整数&nbsp;(或者用__int64) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;QueryPerformanceFrequency&nbsp;(&nbsp;(&nbsp;LARGE_INTEGER&nbsp;*)&nbsp;&amp;m_lPerfCounter&nbsp;)&nbsp;)&nbsp;{&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_bPerFlag&nbsp;=&nbsp;TRUE;&nbsp;</span><span class="comment">//选择系统时钟 </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//原默认值为8的m_dwTimeCount&nbsp;现在就是实时的(n/s)/(f/s)=n/f每帧的频数 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_dwTimeCount&nbsp;=&nbsp;unsigned&nbsp;</span><span class="keyword">long</span><span>(m_lPerfCounter&nbsp;/&nbsp;m_lFramesPerSecond); </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//同样,高精计数器QueryPerformanceCounter&nbsp;获取用于指向当前&quot;CPU运行到现在的嘀哒数(n)&quot;的指针,并把值交给它(m_lNextTime)来指向 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;QueryPerformanceCounter&nbsp;(&nbsp;(&nbsp;LARGE_INTEGER&nbsp;*&nbsp;)&nbsp;&amp;m_lNextTime&nbsp;);&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_dTimeScale&nbsp;=&nbsp;1.0&nbsp;/&nbsp;m_lPerfCounter;&nbsp;&nbsp;</span><span class="comment">//&nbsp;&nbsp;s/n </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">else</span><span>&nbsp;&nbsp;{&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//&nbsp;如果系统不支持高精计数器,没办法了,用回粗糙的timeGetTime多媒体计时器 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_lNextTime&nbsp;=&nbsp;timeGetTime&nbsp;();&nbsp;&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//当然这时高精计频器也没什么作用了,干脆让乘数0.001(相当于m_lPerfCounter取1000) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_dTimeScale&nbsp;=&nbsp;0.001; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//保存为当前帧的时间(这里其实用计数表示时间),相当于初始化了,接下来它们俩将交替出现 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;m_lLastTime&nbsp;=&nbsp;m_lNextTime;&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt">&nbsp;</li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//******开始了!无限循环,除非出错或退出程序 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">while</span><span>&nbsp;(&nbsp;TRUE&nbsp;)&nbsp;{&nbsp;&nbsp; </span></li>    <li class="alt"><span class="comment">//这里我理解为,当有消息发过来,(譬如你最小化啦遮蔽了窗口啦中断(INT)等等, </span></li>    <li><span class="comment">//就先暂停计时器运算,停继续渲染,先处理所有消息,这个延伸起来很复杂,先放放. </span></li>    <li class="alt"><span class="comment">//注意,没有消息来时,PeekMessage会返回一个空值到应用程序，GetMessage会在此时让应用程序休眠。 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;::PeekMessage&nbsp;(&nbsp;&amp;msg,&nbsp;NULL,&nbsp;0,&nbsp;0,&nbsp;PM_NOREMOVE&nbsp;)&nbsp;)&nbsp;{&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">do</span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//如果消息渠里还有消息 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!PumpMessage&nbsp;()&nbsp;) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;ExitInstance&nbsp;(); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">while</span><span>&nbsp;(&nbsp;::PeekMessage&nbsp;(&nbsp;&amp;msg,&nbsp;NULL,&nbsp;0,&nbsp;0,&nbsp;PM_NOREMOVE&nbsp;)&nbsp;); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">else</span><span>&nbsp;{&nbsp;&nbsp;&nbsp;</span><span class="comment">//没消息要处理了,就继续: </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//这个摆明了,如果用QueryPerformanceCounter&nbsp;不行,就用timeGetTime </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;m_bPerFlag&nbsp;)&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;QueryPerformanceCounter&nbsp;(&nbsp;(&nbsp;LARGE_INTEGER&nbsp;*&nbsp;)&nbsp;&amp;m_lCurTime&nbsp;);&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;m_lCurTime=timeGetTime&nbsp;();&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//如果当前计数(代表着时间)到达先前设定的m_lNextTime,譬如从帧1来到帧2&nbsp;: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;m_lCurTime&nbsp;&gt;&nbsp;m_lNextTime&nbsp;)&nbsp;{&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//&nbsp;计算当前时间(刚到达帧2的时间)与刚才帧1开始时间之差,由计数转换为时间表示 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;m_dTimeElapsed&nbsp;=&nbsp;(&nbsp;m_lCurTime&nbsp;-&nbsp;m_lLastTime&nbsp;)&nbsp;&nbsp;*&nbsp;&nbsp;m_dTimeScale; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span class="comment">//&nbsp;现在当前帧(帧2)就成了名义上的&quot;上一帧&quot;了,接下来会变成帧2到帧3 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_lLastTime&nbsp;=&nbsp;m_lCurTime; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span class="comment">//这个m_bAppIsActive在CMainframe类里会经常见到,只有它是TRUE时才渲染 </span></li>    <li class="alt"><span class="comment">//也就是初始化呀屏幕像素设置呀搞好后才开始渲染 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;!pFrame-&gt;m_bAppIsActive&nbsp;) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WaitMessage(); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pFrame-&gt;RenderGLScene&nbsp;(); </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//计算下一帧触发时的计数,即把这个当前计数与刚才高精出来的&quot;每帧的频数(计数)&quot;相加 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_lNextTime&nbsp;=&nbsp;m_lCurTime&nbsp;+&nbsp;m_dwTimeCount;&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="comment">//&nbsp;对应if(m_lCurTime&nbsp;&gt;&nbsp;m_lNextTime)那里 </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="comment">//&nbsp;对应&quot;没消息来了&quot;的那个else </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="comment">//&nbsp;end&nbsp;while </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;msg.wParam;</span><span class="comment">//将返回代码依次返回调用层（退出） </span></li>    <li>&nbsp;</li>    <li class="alt"><span>}&nbsp;</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>int C***App::Run()<br/>{<br/>	if (!m_pMainWnd)<br/>		AfxPostQuitMessage(0);<br/><br/>	MSG msg;<br/>//STATIC_DOWNCAST .....强制类型转换的宏?请看MSDN<br/>//m_pMainWnd 被直接转换为指向CMainFrame类(这编译器转型要求此类必须继承于CObject  - -)的指针了<br/>CMainFrame* pFrame = STATIC_DOWNCAST ( CMainFrame, m_pMainWnd );<br/><br/>//QueryPerformanceFrequency:高精计频器获取一个指向定时器每秒的频率数(m_lPerfCounter ,单位n/s)的指针,并把系统值(CPU时钟频率,每秒嘀哒声的个数)交给它.返回值表明是否支持高精计时<br/>//LARGE_INTEGER,结构体类型,模拟64位有符号的二进制整数 (或者用__int64)<br/>	if ( QueryPerformanceFrequency ( ( LARGE_INTEGER *) &amp;m_lPerfCounter ) ) { <br/>		<br/>		m_bPerFlag = TRUE; //选择系统时钟<br/><br/>//原默认值为8的m_dwTimeCount 现在就是实时的(n/s)/(f/s)=n/f每帧的频数<br/>		m_dwTimeCount = unsigned long(m_lPerfCounter / m_lFramesPerSecond);<br/><br/>//同样,高精计数器QueryPerformanceCounter 获取用于指向当前&quot;CPU运行到现在的嘀哒数(n)&quot;的指针,并把值交给它(m_lNextTime)来指向<br/>		QueryPerformanceCounter ( ( LARGE_INTEGER * ) &amp;m_lNextTime ); <br/>		m_dTimeScale = 1.0 / m_lPerfCounter;  //  s/n<br/>	} else  { <br/><br/>// 如果系统不支持高精计数器,没办法了,用回粗糙的timeGetTime多媒体计时器<br/>		m_lNextTime = timeGetTime ();  <br/><br/>//当然这时高精计频器也没什么作用了,干脆让乘数0.001(相当于m_lPerfCounter取1000)<br/>		m_dTimeScale = 0.001;<br/>	} <br/><br/>//保存为当前帧的时间(这里其实用计数表示时间),相当于初始化了,接下来它们俩将交替出现<br/>	m_lLastTime = m_lNextTime; <br/><br/><br/><br/>//******开始了!无限循环,除非出错或退出程序<br/>	while ( TRUE ) {  <br/>//这里我理解为,当有消息发过来,(譬如你最小化啦遮蔽了窗口啦中断(INT)等等,<br/>//就先暂停计时器运算,停继续渲染,先处理所有消息,这个延伸起来很复杂,先放放.<br/>//注意,没有消息来时,PeekMessage会返回一个空值到应用程序，GetMessage会在此时让应用程序休眠。<br/>		if ( ::PeekMessage ( &amp;msg, NULL, 0, 0, PM_NOREMOVE ) ) { <br/>			do        //如果消息渠里还有消息<br/>			{<br/>				if ( !PumpMessage () )<br/>					return ExitInstance ();<br/>			} <br/>			while ( ::PeekMessage ( &amp;msg, NULL, 0, 0, PM_NOREMOVE ) );<br/>		} else {   //没消息要处理了,就继续:<br/><br/>//这个摆明了,如果用QueryPerformanceCounter 不行,就用timeGetTime<br/>			if ( m_bPerFlag ) {<br/>	QueryPerformanceCounter ( ( LARGE_INTEGER * ) &amp;m_lCurTime );	<br/>			} <br/>			else {<br/>	m_lCurTime=timeGetTime (); <br/>			}<br/><br/>//如果当前计数(代表着时间)到达先前设定的m_lNextTime,譬如从帧1来到帧2 :<br/>			if ( m_lCurTime &gt; m_lNextTime ) { <br/><br/>// 计算当前时间(刚到达帧2的时间)与刚才帧1开始时间之差,由计数转换为时间表示<br/>	m_dTimeElapsed = ( m_lCurTime - m_lLastTime )  *  m_dTimeScale;<br/>				<br/>// 现在当前帧(帧2)就成了名义上的&quot;上一帧&quot;了,接下来会变成帧2到帧3<br/>				m_lLastTime = m_lCurTime;<br/>				<br/>//这个m_bAppIsActive在CMainframe类里会经常见到,只有它是TRUE时才渲染<br/>//也就是初始化呀屏幕像素设置呀搞好后才开始渲染<br/>				if ( !pFrame-&gt;m_bAppIsActive )<br/>					WaitMessage();<br/>				else<br/>					pFrame-&gt;RenderGLScene ();<br/><br/>//计算下一帧触发时的计数,即把这个当前计数与刚才高精出来的&quot;每帧的频数(计数)&quot;相加<br/>				m_lNextTime = m_lCurTime + m_dwTimeCount; <br/><br/>			} // 对应if(m_lCurTime &gt; m_lNextTime)那里<br/><br/>		} // 对应&quot;没消息来了&quot;的那个else<br/><br/>	} // end while<br/>	return msg.wParam;//将返回代码依次返回调用层（退出）<br/><br/>} </pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>好了,接下来的日志就是剖CMainframe了!请看:<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/MFC/2-nehe-frame-vc6.html">ZwqXin:自剖一下自己用的NEHE OpenGL框架(中篇)</a><br /><br/>呃?怎么那么像其实是在讲MFC? 恩,赶忙加回个<a target="_blank" href="http://www.zwqxin.com/catalog.asp?tags=MFC">MFC的Tag</a>先!</p>]]></description><category>VC/MFC</category><comments>http://www.zwqxin.com/archives/MFC/nehe-frame-vc6.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=6</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=6&amp;key=6e434d1d</trackback:ping></item></channel></rss>
