﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-万星星@完美世界-随笔分类-richedit</title><link>http://www.cppblog.com/wlwlxj/category/19335.html</link><description>&lt;div&gt;一个吃软饭的男人!!!!!我只想写程序####&lt;br&gt;师夷之技以制夷   吾尝终日而思矣,不如须臾之所学也&lt;br&gt;喝碗孟婆汤,踏过奈何桥,涅槃&lt;/div&gt;
I've been programming since I was 21. Started with C++, looked at Java, keen on Visual Basic and Visual C++, thinking about .NET and C#.^_^,very ridiculous.</description><language>zh-cn</language><lastBuildDate>Sat, 08 Sep 2012 13:52:52 GMT</lastBuildDate><pubDate>Sat, 08 Sep 2012 13:52:52 GMT</pubDate><ttl>60</ttl><item><title>richedit研究06 – 高效动画刷新</title><link>http://www.cppblog.com/wlwlxj/archive/2012/09/08/189951.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Sat, 08 Sep 2012 10:10:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/09/08/189951.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/189951.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/09/08/189951.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/189951.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/189951.html</trackback:ping><description><![CDATA[<p>最近工作上比较忙，加之编码任务较多，没来得及继续之前的讲解。抽出时间把这最重要的一部分东西做个阐述。行文以基本的编程思维及个人思考过程为线索。</p>
<p>&nbsp;</p>
<p>众所周知，RichEdir强大在于其图文混排（在这里不跟Word、HTML比），其中的图替换为动态图的核心问题就归结于如何高效刷新。我们知道GDI操作是最消耗CPU的，所以刷新整个RichEdit窗口是不可取的，其副作用会导致更严重的闪烁问题。解决问题的思路很简单：类似于拖拽时候在屏幕绘制异或线，我们的动画重绘时不请求RichEdit，而直接在其窗口的DC上绘制当前动画帧，此时缺少是如何确定该OLE的位置，这个是所有问题的关键。先看下面这幅图：</p>
<p><img src="http://images.cnblogs.com/cnblogs_com/wlwel/240246/o_richedit_animate1.png" alt="" width="538" height="363" /></p>
<p>&nbsp;</p>
<p>假定1-5全部都是GIF图片，非GIF可以暂时无视，这个后面大家会非常清楚如何处理。在这个过程中，2不见了，而4是新出现的。对于4新出现时，RichEdit自身肯定会触发其：</p>
<div class="cnblogs_code">
<pre><span style="color: #000000;">Draw(
    DWORD dwDrawAspect, LONG lindex, </span><span style="color: #0000ff;">void</span>*<span style="color: #000000;"> pvAspect,
    DVTARGETDEVICE</span>*<span style="color: #000000;"> ptd, HDC hicTargetDev, HDC hdcDraw,
    LPCRECTL prcBounds, LPCRECTL prcWBounds,
    BOOL (__stdcall </span>*<span style="color: #000000;">pfnContinue)(DWORD_PTR dwContinue),
    DWORD_PTR dwContinue)</span></pre>
</div>
<p>&nbsp;</p>
<p>这个时候，我们知道新的GIF图片进入可视区，可以把它添加到集合中。对于2的动画触发时间到来时，我们可以确定其位置且与可视区比对，发现其不再可视区，则从集合中移除。这样就可以得到一个接近于（略大于）当前视口中的动画控件集合，当有新的动画触发时间到来时，我们可以先检查其是否在可视区，如果不在则不用GDI操作，仅仅更新其当前帧。当然这些工作你也可以不做，但是在动画控件数量大的时候效率可能略有下降，主要是查找的过程（索引、位置）比较耗时。</p>
<p>&nbsp;</p>
<p>如何确定一个OLE的位置呢？由于我们插入OLE都使用了REO_BELOWBASELINE标志，也就是跟当前行的底部对齐，所以OLE左下角位置的精确度对我们来说很重要。看下图：</p>
<p><img src="http://images.cnblogs.com/cnblogs_com/wlwel/240246/o_richedit_animate2.png" alt="" width="363" height="214" /></p>
<p>假设图中黑框是一个OLE对象，其字符索引为CPN，假定第N+1行的第一个字符索引为CPN1，那么OLE左下角坐标={PosFromChar(CPN).x, PosFromChar(CPN1).y }，PosFromChar这个是RichEdit提供的。问题的关键是最后一行怎么计算？此时没有第N+1行。对于这种特殊情况，主要是Y坐标的计算，可以这样考虑：Y=RichEdit内容高度-滚动条位置。猜测: 计算内容高度可能比较耗时，故QQ的聊天消息显示部分强制在底部加了一行，以避免这种情况出现。</p>
<p>&nbsp;</p>
<p>得到左下角位置以后，可能你会觉得就万事大吉了。错！还有一个关键点！我们可以通过OLE的接口GetExtent得到其大小，然而这个大小没有考虑缩放比例，所以你需要根据当前缩放比例进行计算，而这个计算牵扯到浮点数运算，过程中的来回不仅麻烦而且不精确，所以OLE的可视大小要想非常精确是不能通过计算来的。我们前面知道OLE绘制的时候会传入可视范围，假如我们保存下来是不是就可以解决问题了呢？当然，显然，你可以试试！<br /><br /><span style="font-family: verdana, Arial, Helvetica, sans-serif; background-color: #ffffff; ">这些问题主要原因是RichEdit的很多接口方法没有暴露，而Win8的SDK会做重大升级，很多之前的问题都会变成不是问题，或许还会引起更多的新特性，但是动画本身的逻辑还是需要自己实现，或者会简单许多，至于多少我还尚不清楚，但是目前来看这种方案效率足够！</span>&nbsp;<br /></p>
<p>&nbsp;</p>
<p>到了这里，核心技术应该大白天下，整个过程，我追求了位置的精准度，并据此获得最小可视集合进行刷新优化。</p>
<p>&nbsp;</p>
<p>最新SDK&amp;Demo，参见：<a href="http://code.google.com/p/im-solution/">http://code.google.com/p/im-solution/</a>。希望你会喜欢！</p><img src ="http://www.cppblog.com/wlwlxj/aggbug/189951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-09-08 18:10 <a href="http://www.cppblog.com/wlwlxj/archive/2012/09/08/189951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>richedit研究 – 正式版1.0.0发布</title><link>http://www.cppblog.com/wlwlxj/archive/2012/09/06/189750.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Thu, 06 Sep 2012 14:25:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/09/06/189750.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/189750.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/09/06/189750.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/189750.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/189750.html</trackback:ping><description><![CDATA[最近忙里偷闲对之前的设计做了大量重构，其间转辗反侧体会到过度设计之苦。然而这个产品是我第二用心持续之作，第一的当然是自己商业化盈利产品，实乃无心插柳，感觉这2年自己设计能力进步神速，打算重新架构。前面的x-framework界面框架停止维护许久，深感自责，但是我绝没有放弃信念，卷土重来之日是必须的。<br />
<br />
先谈下richedit我做的工作，主要是参照QQ的功能进行设计，分为2个部分：texthost和richole，前者实现无窗口的richedit，后者实现动画控件。这些东西可以说网上可见的鲜有正确的方法论，很多都是饮鸩止渴之手段，我确信自己的手法是非常科学的。<br />
<br />
目前的实现：<br />
1.动画控件<br />
2.拷贝粘贴：支持QQ互通 &nbsp;支持HTML格式(网页 word等)互通 &nbsp;Shell拖放互通 &nbsp;画图程序的互通<br />
3.窗口、无窗口控件统一操作接口<br />
4.增强的扩展能力<br />
<br />
现有的不足和完善：<br />
1.粘贴网页中图像的下载过程是堵塞式下载<br />
2.炫彩字体完善<br />
3.新闻摘要（仿QQ）OLE实现<br />
<br />
效果图：<br />
<img src="http://www.cppblog.com/images/cppblog_com/wlwlxj/8028/o_im_richedit_donews.png" alt="" /><br />
<br />
SDK&amp;Demo<a href="http://www.cppblog.com/files/wlwlxj/im_richedit_1.0.0.rar">下载</a><br />
<br />
<br />
无论如何，对现在的设计（架构&amp;稳定性）到达了一个满意效果，最小的依赖和最通用的编码，觉得可以拿出来show一下。如果您有任何建议都可以在下面提出或者给我email（索要源码除外，技术讲解我会尽快展开，按照之前的大纲）。总之一切都是围绕QQ效果实现，为实现IM类聊天展示提供解决方案，武装到牙齿。<img src ="http://www.cppblog.com/wlwlxj/aggbug/189750.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-09-06 22:25 <a href="http://www.cppblog.com/wlwlxj/archive/2012/09/06/189750.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>richedit研究 – 拷贝&amp;粘贴初步实现效果</title><link>http://www.cppblog.com/wlwlxj/archive/2012/08/26/188342.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Sun, 26 Aug 2012 09:15:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/08/26/188342.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/188342.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/08/26/188342.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/188342.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/188342.html</trackback:ping><description><![CDATA[<span style="font-size: 14pt; ">花了四天时间，再次对QQ的剪切板格式做了深入研究，对im_richedit做了一次重构使得richframe作为抽象的支持动画功能占位块，派生出richpicture。从基本功能上来讲，可以实现qq消息框的功能。</span><br />
<br />
<span style="font-size: 14pt; ">
我支持的剪贴板格式如下：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">enum</span>&nbsp;FETCINDEX&nbsp;{<br />
&nbsp;&nbsp;kFETCINDEXUnicode,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Unicode&nbsp;文本</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;kFETCINDEXAnsi,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;ANSI&nbsp;文本</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;kFETCINDEXDIB,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;DIB</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;kFETCINDEXHDROP,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;HDROP</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;kFETCINDEXHTML,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;HTML</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;kFETCINDEXIMRichEdit,&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;自定义格式</span><span style="color: #008000; "><br />
</span>};</div>
<br />
<span style="font-size: 14pt; ">未来打算扩展的OLE类型如下：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">enum</span>&nbsp;IMRichElementType&nbsp;{<br />
&nbsp;&nbsp;kIMRichElementText,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;字符串</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;kIMRichElementCustomPicture,&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;自定义图片</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;kIMRichElementSystemPicture,&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;系统图片</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;kIMRichElementFancyCharacter&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;炫彩字符</span><span style="color: #008000; "><br />
</span>};</div>
<br />
<span style="font-size: 14pt; ">时间紧张，很多细节处理不完善，上周还初步实现了chromium的windowless版的embed方案，新的一周要努力完善。</span><br />
<br />
<span style="font-size: 14pt; ">
效果图如下（支持HTML拷贝咯！！！）：<br />
</span><br />
<img src="http://www.cppblog.com/images/cppblog_com/wlwlxj/8028/o_copy_html.png" alt="" /><br />
<br />
<span style="font-size: 14pt; ">执行文件</span><a href="http://www.cppblog.com/files/wlwlxj/copynpaste.rar"><span style="font-size: 14pt; ">下载</span></a><br />
<br />
<span style="font-size: 14pt; ">
技术讲解会在现在特性细节完成后继续。扯点别的，继chrome os的shell放弃web实现后，fb也倾向native app而暂缓html5，似乎给html5泼了一小点冷水。新技术的稳定需要一个过程，而商业公司往往看重的是当前的运营、盈利。当然html5本身仅仅是加入了一些语义、植入了一些sdk，而作为Windows开发者擅长的也是对sdk封装而非语言层面的封装，相比之下，web方面我比较热衷用html5新特性(canvas)来实现UI，而对于原生的html元素拼凑界面有点摸不着门道。</span><img src ="http://www.cppblog.com/wlwlxj/aggbug/188342.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-08-26 17:15 <a href="http://www.cppblog.com/wlwlxj/archive/2012/08/26/188342.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>richedit研究05 – 动画控件</title><link>http://www.cppblog.com/wlwlxj/archive/2012/08/05/186352.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Sun, 05 Aug 2012 11:18:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/08/05/186352.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/186352.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/08/05/186352.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/186352.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/186352.html</trackback:ping><description><![CDATA[<h2><p><span style="font-family: 宋体; font-weight: normal;"></span></p><p><span style="font-family: 宋体; font-size: 14pt; font-weight: normal;"></span></p><p><span style="font-family: 宋体; font-size: 14pt; font-weight: normal;">很近没有更新了，闲话少说，直奔主题。</span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal;"><span style="font-family: 宋体; font-size: 14pt; ">微软做任何技术的思路：在实现一个标准的时候，往往预留出一个通用的扩展机制。呃，貌似很多大公司都是如此，通过扩展把开发者跟自己捆绑。举例：微软的</span><span style="font-size: 14pt; ">ie</span><span style="font-family: 宋体; font-size: 14pt; ">可以嵌入</span><span style="font-size: 14pt; ">ActiveX</span><span style="font-family: 宋体; font-size: 14pt; ">控件、可以用</span><span style="font-size: 14pt; ">BHO</span><span style="font-family: 宋体; font-size: 14pt; ">扩展；</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">中支持</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">扩展。这种扩展机制主要是基于其</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">框架，这也是微软操作系统框架的基石。开发层面目前的趋势是，淡化</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">强调</span><span style="font-size: 14pt; ">.NET</span><span style="font-family: 宋体; font-size: 14pt; ">，有一种无奈叫骑虎难下，有一种错误叫脱离群众，在开发平台、技术百花齐放，开发资源极大丰富的今天，开发者对微软的依赖已经不那么强烈，以至于现在的</span><span style="font-size: 14pt; ">Windows</span><span style="font-family: 宋体; font-size: 14pt; ">开发者有点小苦逼，尤其是</span><span style="font-size: 14pt; ">C++</span><span style="font-family: 宋体; font-size: 14pt; ">开发者有一种被抛弃的感觉，扯远了。</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal;"><span style="font-family: 宋体; font-size: 14pt; ">谈谈</span><span style="font-size: 14pt; ">OLE/COM/ACTIVEX</span><span style="font-family: 宋体; font-size: 14pt; ">的关系。很模糊，有一些说不清，有历史原因，也有普及程度关系。侯捷说玩模板有三境界：会用标准库，读懂标准库源码，会用模板做设计。我觉得</span><span style="font-size: 14pt; ">COM</span><span style="font-family: 宋体; font-size: 14pt; ">相关的也是如此，使用</span><span style="font-size: 14pt; ">COM</span><span style="font-family: 宋体; font-size: 14pt; ">组件往往是容易。我的理解：</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">是技术规范，</span><span style="font-size: 14pt; ">COM</span><span style="font-family: 宋体; font-size: 14pt; ">是语言规范，而</span><span style="font-size: 14pt; ">ACTIVEX</span><span style="font-family: 宋体; font-size: 14pt; ">则是用这</span><span style="font-size: 14pt; ">2</span><span style="font-family: 宋体; font-size: 14pt; ">东东来实做的可服用组件的称谓。对于</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">的支撑主要在</span><span style="font-size: 14pt; ">MFC</span><span style="font-family: 宋体; font-size: 14pt; ">库中，而</span><span style="font-size: 14pt; ">ATL</span><span style="font-family: 宋体; font-size: 14pt; ">库则是更纯粹的</span><span style="font-size: 14pt; ">OLE/COM</span><span style="font-family: 宋体; font-size: 14pt; ">框架。</span><span style="font-size: 14pt; ">MFC</span><span style="font-family: 宋体; font-size: 14pt; ">跟</span><span style="font-size: 14pt; ">OFFICE</span><span style="font-family: 宋体; font-size: 14pt; ">有一衣带水的关系，</span><span style="font-size: 14pt; ">OFFICE</span><span style="font-family: 宋体; font-size: 14pt; ">的应用框架促使着</span><span style="font-size: 14pt; ">MFC</span><span style="font-family: 宋体; font-size: 14pt; ">的发展（早期如此，但</span><span style="font-size: 14pt; ">UI</span><span style="font-family: 宋体; font-size: 14pt; ">方面早已分道扬镳），</span><span style="font-size: 14pt; ">OFFICE</span><span style="font-family: 宋体; font-size: 14pt; ">的应用模型也就是</span><span style="font-size: 14pt; ">MFC</span><span style="font-family: 宋体; font-size: 14pt; ">的应用开发模型。</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal;"><span style="font-family: 宋体; font-size: 14pt; ">之所以提及</span><span style="font-size: 14pt; ">MFC</span><span style="font-family: 宋体; font-size: 14pt; ">又</span><span style="font-size: 14pt; ">OFFICE</span><span style="font-family: 宋体; font-size: 14pt; ">，只是想说通用的扩展机制没有那么多条条框框，即便是</span><span style="font-size: 14pt; ">ACTIVEX</span><span style="font-family: 宋体; font-size: 14pt; ">框架这种东西。对于</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">实践，也就是微软最热衷，其中</span><span style="font-size: 14pt; ">Windows</span><span style="font-family: 宋体; font-size: 14pt; ">操作系统和</span><span style="font-size: 14pt; ">OFFICE</span><span style="font-family: 宋体; font-size: 14pt; ">系列软件要最典型，其它则很牵强。</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">技术标准接口只有极少数是必须实现，而大部分则是可选实现或者部分实现，其中</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">更是如此。</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">对服务器和客户端都做了行为规范，如果一方（一般是服务器）自行决定如何实施，则另一方也只需对应实现。</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal;"><span style="font-family: 宋体; font-size: 14pt; ">呃，我说了这么多，只是为了阐述我的险恶用心，或许没人明白。</span><span style="font-size: 14pt; ">ATL</span><span style="font-family: 宋体; font-size: 14pt; ">框架定义了四个标准导出函数用于规范注册、反注册、加载、卸载，这些跟实际的</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">功能无关，尤其是在</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">扩展中。或许你在网上诸多示例中看到用</span><span style="font-size: 14pt; ">ATL</span><span style="font-family: 宋体; font-size: 14pt; ">模板创建一个控件然后如何简单的插入位图就以为掌握了核心科技，那么我就要泼冷水了，这些东西无关大局。既然</span><span style="font-size: 14pt; ">Windows</span><span style="font-family: 宋体; font-size: 14pt; ">能用</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">搭建框架，既然</span><span style="font-size: 14pt; ">MFC</span><span style="font-family: 宋体; font-size: 14pt; ">可以实践</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">，那么我们也可以用纯正的</span><span style="font-size: 14pt; ">C++</span><span style="font-family: 宋体; font-size: 14pt; ">代码玩</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">，我的意思无非就是没必要遵循</span><span style="font-size: 14pt; ">ATL</span><span style="font-family: 宋体; font-size: 14pt; ">，也没必要一定去注册一个东西，问题的核心不是这些东西，目前我们仅仅是为了插入一个动画。</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal;"><span style="font-size: 14pt; ">Richedit</span><span style="font-family: 宋体; font-size: 14pt; ">是一个不完全的</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">实践，前面提到能完全实践</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">的框架不多。因为</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">实现了图文混排，所以在</span><span style="font-size: 14pt; ">IM</span><span style="font-family: 宋体; font-size: 14pt; ">领域很受欢迎，尤其是早期（现在基于</span><span style="font-size: 14pt; ">chromium</span><span style="font-family: 宋体; font-size: 14pt; ">的扩展或许可以改变现状）。</span><span style="font-size: 14pt; ">Richedit</span><span style="font-family: 宋体; font-size: 14pt; ">是一个容器，可以容纳</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">控件进入，典型的扩展就是动画控件。基于</span><span style="font-size: 14pt; ">ATL</span><span style="font-family: 宋体; font-size: 14pt; ">框架开发，你可以实现一个标准的控件，但当你面对一个非标准的容器时，那些条条框框显得不是那么重要，这也是为什么能做好动画控件不容易的原因。</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal;"><span style="font-family: 宋体; font-size: 14pt; ">根据我的调查（呃，通过实践，通过</span><span style="font-size: 14pt; ">QueryInterface</span><span style="font-family: 宋体; font-size: 14pt; ">观察），我发现实现一个</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">中的动画控件只需要实现二个接口：</span><span style="font-size: 14pt; ">IOleObject</span><span style="font-family: 宋体; font-size: 14pt; ">、</span><span style="font-size: 14pt; ">IViewObject2</span><span style="font-family: 宋体; font-size: 14pt; ">，前者为了融入到</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">环境中，后者为了渲染显示。由于</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">默认只喜好无窗口模式，所以针对</span><span style="font-size: 14pt; ">IOleInPlaceSiteWindowless</span><span style="font-family: 宋体; font-size: 14pt; ">之类的，你去实现意义也不大，因为人家容器不认你，当然还有</span><span style="font-size: 14pt; ">IPersist</span><span style="font-family: 宋体; font-size: 14pt; ">系列接口，对于标准的环境有用（比如</span><span style="font-size: 14pt; ">IDE</span><span style="font-family: 宋体; font-size: 14pt; ">），但这里并不是很需要，所以认清核心问题能减少很多困惑。更显然的是我的控件没有用</span><span style="font-size: 14pt; ">ATL</span><span style="font-family: 宋体; font-size: 14pt; ">框架，因为此控件脱离了</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">环境生存的意义也不大，更有甚者我没必要让使其成为标准（也没可能），仅仅是为了在一个系统中的</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">中更好地展示。实现的接口越少，引入的麻烦越少，这样才能使精力集中在主要问题上。</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal; "><span style="font-family: 宋体; font-size: 14pt; ">综上所述，我的控件是一个</span><span style="font-size: 14pt; ">C++</span><span style="font-family: 宋体; font-size: 14pt; ">类，只实现了两个接口：</span></span></p>  <p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="font-weight: normal;">X_BEGIN_INTERFACE_MAP(IMRichPicture,&nbsp;ObjectRootBase)<br />&nbsp;&nbsp;X_INTERFACE_PART(IMRichPicture,&nbsp;IID_IOleObject,&nbsp;OleObject)<br />&nbsp;&nbsp;X_INTERFACE_PART(IMRichPicture,&nbsp;IID_IViewObject,&nbsp;ViewObject)<br />&nbsp;&nbsp;X_INTERFACE_PART(IMRichPicture,&nbsp;IID_IViewObject2,&nbsp;ViewObject)<br />X_END_INTERFACE_MAP()</span></div><p>&nbsp;</p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal;"><span style="font-family: 宋体; font-size: 14pt; ">其中大部分接口都可以无视，因为我们只需要这个控件在</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">中能够占位（长宽），能够展示（效率关键），至于其他的可编程、在位激活、对象识别都不重要。我观察了</span><span style="font-size: 14pt; ">QQ</span><span style="font-family: 宋体; font-size: 14pt; ">的动画控件，呃，比现在网上流行的要改变很多（网上内容没有与时俱进）。现在的</span><span style="font-size: 14pt; ">`QQ</span><span style="font-family: 宋体; font-size: 14pt; ">的动画控件很简单（后面会讲述如何找到这个控件），看起来只是作为一个占位工具，如何触发动画则是由</span><span style="font-size: 14pt; ">host</span><span style="font-family: 宋体; font-size: 14pt; ">控制。观其接口：</span></span></p>  <p align="left"></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="font-weight: normal;"><span style="color: #0000ff; font-size: 10pt; ">interface</span><span style="font-size: 10pt; ">&nbsp;IRichFrameObj&nbsp;:&nbsp;IDispatch&nbsp;{</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;};</span><br /><span style="color: #0000ff; font-size: 10pt; ">interface</span><span style="font-size: 10pt; ">&nbsp;IRichPicObj&nbsp;:&nbsp;IDispatch&nbsp;{</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[id(</span><span style="font-size: 10pt; ">0x00000001</span><span style="font-size: 10pt; ">),&nbsp;propput,&nbsp;helpstring(</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">property&nbsp;src</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">)]</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;src([</span><span style="color: #0000ff; font-size: 10pt; ">in</span><span style="font-size: 10pt; ">]&nbsp;BSTR&nbsp;rhs);</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[id(</span><span style="font-size: 10pt; ">0x00000002</span><span style="font-size: 10pt; ">),&nbsp;propput,&nbsp;helpstring(</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">property&nbsp;static</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">)]</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;na([</span><span style="color: #0000ff; font-size: 10pt; ">in</span><span style="font-size: 10pt; ">]&nbsp;</span><span style="color: #0000ff; font-size: 10pt; ">long</span><span style="font-size: 10pt; ">&nbsp;rhs);</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[id(</span><span style="font-size: 10pt; ">0x00000003</span><span style="font-size: 10pt; ">),&nbsp;propput,&nbsp;helpstring(</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">property&nbsp;autoHeight</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">)]</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;autoHeight([</span><span style="color: #0000ff; font-size: 10pt; ">in</span><span style="font-size: 10pt; ">]&nbsp;</span><span style="color: #0000ff; font-size: 10pt; ">long</span><span style="font-size: 10pt; ">&nbsp;rhs);</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[id(</span><span style="font-size: 10pt; ">0x00000004</span><span style="font-size: 10pt; ">),&nbsp;propput,&nbsp;helpstring(</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">property&nbsp;autoWidth</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">)]</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;autoWidth([</span><span style="color: #0000ff; font-size: 10pt; ">in</span><span style="font-size: 10pt; ">]&nbsp;</span><span style="color: #0000ff; font-size: 10pt; ">long</span><span style="font-size: 10pt; ">&nbsp;rhs);</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[id(</span><span style="font-size: 10pt; ">0x00000005</span><span style="font-size: 10pt; ">),&nbsp;propput,&nbsp;helpstring(</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">property&nbsp;maxAutoWidth</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">)]</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;maxAutoWidth([</span><span style="color: #0000ff; font-size: 10pt; ">in</span><span style="font-size: 10pt; ">]&nbsp;</span><span style="color: #0000ff; font-size: 10pt; ">int</span><span style="font-size: 10pt; ">&nbsp;rhs);</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[id(</span><span style="font-size: 10pt; ">0x00000006</span><span style="font-size: 10pt; ">),&nbsp;propput,&nbsp;helpstring(</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">property&nbsp;onerror</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">)]</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;onerror([</span><span style="color: #0000ff; font-size: 10pt; ">in</span><span style="font-size: 10pt; ">]&nbsp;BSTR&nbsp;rhs);</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[id(</span><span style="font-size: 10pt; ">0x00000007</span><span style="font-size: 10pt; ">),&nbsp;propput,&nbsp;helpstring(</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">property&nbsp;objid</span><span style="font-size: 10pt; ">"</span><span style="font-size: 10pt; ">)]</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;objid([</span><span style="color: #0000ff; font-size: 10pt; ">in</span><span style="font-size: 10pt; ">]&nbsp;BSTR&nbsp;rhs);</span><br /><span style="font-size: 10pt; ">&nbsp;&nbsp;&nbsp;&nbsp;};</span></span></div><p>&nbsp;</p>  <p><span style="font-weight: normal;"><span style="font-family: Courier; font-size: 14pt; ">IrichFrameObj</span><span style="font-family: 宋体; font-size: 14pt; ">的作用我不是很理解</span><span style="font-family: 宋体; font-size: 14pt; ">，</span><span style="font-family: 宋体; font-size: 14pt; ">居然一个接口都没有</span><span style="font-family: 宋体; font-size: 14pt; ">，</span><span style="font-family: 宋体; font-size: 14pt; ">而后者模糊的能够理解一些</span><span style="font-family: 宋体; font-size: 14pt; ">：</span><span style="font-family: Courier; font-size: 14pt; ">src</span><span style="font-family: 宋体; font-size: 14pt; ">大概就是动画图片路径，</span><span style="font-family: Courier; font-size: 14pt; ">auto</span><span style="font-family: 宋体; font-size: 14pt; ">系列是为了动态缩放。现在的</span><span style="font-family: Courier; font-size: 14pt; ">QQ</span><span style="font-family: 宋体; font-size: 14pt; ">只允许一个自定义动画，依据老衲猜测，因为自定义动画往往是截图，比较大，在一行容易引起点击时视图跳跃。再有其他的属性是为了识别所用，无法推测具体行为。</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-weight: normal;"><span style="font-family: 宋体; font-size: 14pt; ">呃，事情看起来没有那么复杂，的确，我只实现了</span><span style="font-size: 14pt; ">2</span><span style="font-family: 宋体; font-size: 14pt; ">个接口，而其中大部分也都是返回</span><span style="font-size: 14pt; ">E_NOTIMPL</span><span style="font-family: 宋体; font-size: 14pt; ">，因为</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">确实没有那么标准，你实现的再标准也无济于事。当然</span><span style="font-size: 14pt; ">richeit</span><span style="font-family: 宋体; font-size: 14pt; ">也在更新，</span><span style="font-size: 14pt; ">win8</span><span style="font-family: 宋体; font-size: 14pt; ">的</span><span style="font-size: 14pt; ">sdk</span><span style="font-family: 宋体; font-size: 14pt; ">对其改动最大，但</span><span style="font-size: 14pt; ">win7</span><span style="font-family: 宋体; font-size: 14pt; ">的</span><span style="font-size: 14pt; ">sdk</span><span style="font-family: 宋体; font-size: 14pt; ">也暴露了一些更早的功能，这或许是目前实现的最大亮点（技术含量，高风险高回报，一般人难以置信）。</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-family: 宋体; font-size: 14pt; font-weight: normal;">对于动画控件阐述到此为止，或许很多人会很失望，但也仅仅如此，因为它本身什么都没有，尤其是在你真正明白之后，所以这里或许你会很失望，但是真正的内容就这么多，我也不知怎么添油加醋。</span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-family: 宋体; font-size: 14pt; font-weight: normal;">最后一个建议，希望尊重所有的玩技术的：</span></p>  <p><span style="font-weight: normal;"><span style="font-size: 14pt; ">1</span><span style="font-family: 宋体; font-size: 14pt; ">、中国官本位思想很严重，技术搞得不错（或许是运气）立马转管理</span></span></p>  <p><span style="font-weight: normal;"><span style="font-size: 14pt; ">2</span><span style="font-family: 宋体; font-size: 14pt; ">、文人相轻</span></span></p>  <p><span style="font-weight: normal;"><span style="font-size: 14pt; ">3</span><span style="font-family: 宋体; font-size: 14pt; ">、资本运作，体制运作，技术作用不明显</span></span></p>  <p><span style="font-weight: normal;"><span style="font-size: 14pt; ">4</span><span style="font-family: 宋体; font-size: 14pt; ">、吃饱肚皮，难以维系理想</span></span></p>  <p><span style="font-weight: normal;">&nbsp;</span></p>  <p><span style="font-family: 宋体; font-size: 14pt; font-weight: normal;">大道无形，大音希声，牛叉的技术很多都是巧工，而真正的产业才是大无畏的研发，我们只不过在投机取巧。</span></p><p><span style="font-family: 宋体; font-size: 14pt; "></span></p></h2><img src ="http://www.cppblog.com/wlwlxj/aggbug/186352.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-08-05 19:18 <a href="http://www.cppblog.com/wlwlxj/archive/2012/08/05/186352.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>richedit研究 – 阶段性成果展示</title><link>http://www.cppblog.com/wlwlxj/archive/2012/07/01/180982.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Sun, 01 Jul 2012 02:25:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/07/01/180982.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/180982.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/07/01/180982.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/180982.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/180982.html</trackback:ping><description><![CDATA[<span style="font-size: 14pt; ">实际的richedit研究过程中，遇到了各种疑难杂症，真是不容易。比如：</span><br />
// RichEdit使用注意:<br />
<div>// &nbsp; 1.设置CFE_LINK后立即调用AutoURLDetect会导致RichEdit解析当前Word是否为链接.</div>
<div>// &nbsp; &nbsp; 如果想避免这种情况, 必须在这CFE_LINK后插入空格以便把Word区分出来.</div>
<div>// &nbsp; 2.想要对ITextServices发送EM_SCROLLCARET消息, 必须设置ES_NOHIDESEL风格, 或者</div>
<div>// &nbsp; &nbsp; 发送EM_HIDESELECTION消息改变设置(自动滚动到底部功能).<br />
<br />
<span style="font-size: 14pt; ">同样在实现Windowless的richedit的时候，仅仅实现ITextHost接口看上去很美丽，实际上却不能完美，需要用到新的接口：ITextHost2。还有QQ聊天框里面的新闻摘要信息展示OLE，我猜应该是用到了新的REO特性：REO_OWNERDRAWSELECT。这些都是从VS2010的Platform SDK中翻出，richedit的功能在兼容性升级，而开发库却迟迟不更新（？或许我不知道），可想腾讯有如此有心人不断的尝试新特性。昨天无意发现一MSDN博客，此人负责richedit相关的工作以及Math编辑，想要对这方面开发了解的可以到：</span><a href="http://blogs.msdn.com/b/murrays/" style="color: #6fbc4c; font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><span style="font-size: 14pt; ">http://blogs.msdn.com/b/murrays/</span></a><span style="font-size: 14pt; ">。</span><br />
<br />
<span style="font-size: 14pt; ">这一篇不涉及到具体的技术，有兴趣</span><a href="http://www.cppblog.com/files/wlwlxj/im.rar"><span style="font-size: 14pt; ">下载</span></a><span style="font-size: 14pt; ">看看，包括窗口和无窗口的实现，截图一张，满屏Gif性能还是非常强劲的：</span><br />
<br />
<img src="http://www.cppblog.com/images/cppblog_com/wlwlxj/8028/o_im_richedit_effect.png" alt="" width="900" height="355" /></div><img src ="http://www.cppblog.com/wlwlxj/aggbug/180982.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-07-01 10:25 <a href="http://www.cppblog.com/wlwlxj/archive/2012/07/01/180982.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>richedit研究04 – 高效时钟</title><link>http://www.cppblog.com/wlwlxj/archive/2012/06/24/180034.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Sun, 24 Jun 2012 07:51:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/06/24/180034.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/180034.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/06/24/180034.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/180034.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/180034.html</trackback:ping><description><![CDATA[<p><span style="font-family: 宋体; font-size: 14pt; ">上一次，我们可以获取到图片动画帧之间的时间间隔，如果想让动画转起来，就必须有时钟。插入的图片动画数量可能会比较多，因此要想不影响性能，时钟必须很轻量级而且要很高效。</span></p>  <p>&nbsp;</p>  <p><span style="font-size: 14pt; ">Windows</span><span style="font-family: 宋体; font-size: 14pt; ">平台上实现时钟的方式五花八门，你可以使用窗口相关的</span><span style="font-size: 14pt; ">SetTimer</span><span style="font-family: 宋体; font-size: 14pt; ">来设置一个时钟，也可以自己开辟线程来做等待触发模拟时钟，而</span><span style="font-size: 14pt; ">Chromium</span><span style="font-family: 宋体; font-size: 14pt; ">封装的要更加</span><span style="font-size: 14pt; ">C++</span><span style="font-family: 宋体; font-size: 14pt; ">对象化一些：依托</span><span style="font-size: 14pt; ">Windows</span><span style="font-family: 宋体; font-size: 14pt; ">窗口消息，抽象出延迟任务的概念。这种手法几年前我也曾经考虑过，只是对其中下次最短触发时间计算以及更新的算法和设计都有力不从心，最终得出的是误差很大的精简版：选择固定的最小时间片为最小触发单位，对很小的时间间隔误差很明显。</span></p>  <p>&nbsp;</p>  <p><span style="font-size: 14pt; ">Windows</span><span style="font-family: 宋体; font-size: 14pt; ">有</span><span style="font-size: 14pt; ">Timer Queues</span><span style="font-family: 宋体; font-size: 14pt; ">用来实现高效的异步时钟，比较奇怪的是这组</span><span style="font-size: 14pt; ">API</span><span style="font-family: 宋体; font-size: 14pt; ">用的貌似并不多。我们知道每个进程都有一个默认的线程池，可以在其中执行一些</span><span style="font-size: 14pt; ">Work Items</span><span style="font-family: 宋体; font-size: 14pt; ">，时钟队列和等待操作也都会用到这个线程池。</span><span style="font-size: 14pt; ">timer-queue</span><span style="font-family: 宋体; font-size: 14pt; ">中的</span><span style="font-size: 14pt; ">timers</span><span style="font-family: 宋体; font-size: 14pt; ">创建和销毁都很轻量高效，因此我选择了它。</span></p>  <p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">每个</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">图片对象在设置图片之后，如果发现是多帧的，就需要启动动画，创建时钟：</span></p>  <p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->ATLVERIFY(CreateTimerQueueTimer(&amp;timer_,&nbsp;NULL,<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;WaitOrTimerCallback,<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;callback_parameter_.<span style="color: #0000FF; ">get</span>(),<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;image_-&gt;GetFrameDelay(current_frame_),<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;0,&nbsp;WT_EXECUTEDEFAULT));</div><p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">这里</span><span style="font-size: 14pt; ">timer_</span><span style="font-family: 宋体; font-size: 14pt; ">是返回值，返回新建的时钟对象，可以在</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">对象销毁或者回调函数中进行删除，而删除操作会等待回调执行完毕才返回。传递</span><span style="font-size: 14pt; ">TimerQueue</span><span style="font-family: 宋体; font-size: 14pt; ">为</span><span style="font-size: 14pt; ">NULL</span><span style="font-family: 宋体; font-size: 14pt; ">表示使用系统的队列。</span><span style="font-size: 14pt; ">Period</span><span style="font-family: 宋体; font-size: 14pt; ">为</span><span style="font-size: 14pt; ">0</span><span style="font-family: 宋体; font-size: 14pt; ">表示只触发一次，触发时间为</span><span style="font-size: 14pt; ">image_-&gt;GetFrameDelay(current_frame_)</span><span style="font-family: 宋体; font-size: 14pt; ">。由于回调函数</span><span style="font-size: 14pt; ">WaitOrTimerCallback</span><span style="font-family: 宋体; font-size: 14pt; ">是在线程池的线程中执行，所以更新操作需要同步到动画图片的创建线程中。</span><span style="font-size: 14pt; ">callback_parameter_</span><span style="font-family: 宋体; font-size: 14pt; ">包含有上一节提及的</span><span style="font-size: 14pt; ">ThreadState</span><span style="font-family: 宋体; font-size: 14pt; ">对象以及动画</span><span style="font-size: 14pt; ">OLE</span><span style="font-family: 宋体; font-size: 14pt; ">对象指针，</span><span style="font-size: 14pt; ">ThreadState</span><span style="font-family: 宋体; font-size: 14pt; ">创建的时候会同时创建一个隐藏窗口用于工作者线程向</span><span style="font-size: 14pt; ">UI</span><span style="font-family: 宋体; font-size: 14pt; ">线程同步操作：</span></p>  <p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->VOID&nbsp;CALLBACK&nbsp;IMRichPicture::WaitOrTimerCallback(PVOID&nbsp;lpParameter,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BOOLEAN&nbsp;TimerOrWaitFired)&nbsp;{<br />&nbsp;&nbsp;ATLASSERT(TimerOrWaitFired&nbsp;==&nbsp;TRUE);<br />&nbsp;<br />&nbsp;&nbsp;IMRichPicture::CallbackParameter*&nbsp;parameter&nbsp;=<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reinterpret_cast&lt;IMRichPicture::CallbackParameter*&gt;(lpParameter);<br />&nbsp;&nbsp;ATLASSERT(parameter);<br />&nbsp;&nbsp;parameter-&gt;thread_state-&gt;UpdatePictureFrame(parameter-&gt;picture);<br />}</div><p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">下面是</span><span style="font-size: 14pt; ">UpdatePictureFrame</span><span style="font-family: 宋体; font-size: 14pt; ">的实现：</span></p>  <p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">void</span>&nbsp;IMThreadState::UpdatePictureFrame(IMRichPicture*&nbsp;picture)&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;{<br />&nbsp;&nbsp;PostMessage(message_window_,&nbsp;kMessageUpdatePictureFrame,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reinterpret_cast&lt;WPARAM&gt;(picture-&gt;richedit()),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reinterpret_cast&lt;LPARAM&gt;(picture));<br />}</div><p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">这样绕一大圈子，是为了利用</span><span style="font-size: 14pt; ">Timer Queues</span><span style="font-family: 宋体; font-size: 14pt; ">的同时保证图片的更新操作是在</span><span style="font-size: 14pt; ">UI</span><span style="font-family: 宋体; font-size: 14pt; ">线程中执行，因为图片被插入也是发生在</span><span style="font-size: 14pt; ">UI</span><span style="font-family: 宋体; font-size: 14pt; ">线程，即动画控件创建于</span><span style="font-size: 14pt; ">UI</span><span style="font-family: 宋体; font-size: 14pt; ">线程，为了避免加锁带来的麻烦以及死锁的可能性，不应该轻易去加锁，尽量利用操作系统提供的基础设施来实现。这里需要注意的是隐藏窗口接收到</span><span style="font-size: 14pt; ">kMessageUpdatePictureFrame</span><span style="font-family: 宋体; font-size: 14pt; ">消息时，</span><span style="font-size: 14pt; ">richedit</span><span style="font-family: 宋体; font-size: 14pt; ">窗口可能已不存在或者动画控件已经销毁，因此使用指针前，需要判断对象是否还存在：</span></p>  <p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">case</span>&nbsp;kMessageUpdatePictureFrame:&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IMRichEditImpl*&nbsp;richedit&nbsp;=&nbsp;reinterpret_cast&lt;IMRichEditImpl*&gt;(wparam);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IMRichPicture*&nbsp;picture&nbsp;=&nbsp;reinterpret_cast&lt;IMRichPicture*&gt;(lparam);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(IMThreadState::current()-&gt;HasRichEdit(richedit))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;richedit-&gt;OnUpdatePictureFrame(picture);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br />}</div><p>&nbsp;</p><img src ="http://www.cppblog.com/wlwlxj/aggbug/180034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-06-24 15:51 <a href="http://www.cppblog.com/wlwlxj/archive/2012/06/24/180034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>richedit研究03 – 高效图片管理</title><link>http://www.cppblog.com/wlwlxj/archive/2012/06/17/179142.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Sun, 17 Jun 2012 01:48:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/06/17/179142.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/179142.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/06/17/179142.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/179142.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/179142.html</trackback:ping><description><![CDATA[<p><span style="font-family: 宋体; font-size: 14pt; ">这里提及高效稍许有些夸张，仅为应景，因为本身就没有太多高科技，权且作为一种有效的实现。</span></p>  <p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">首先是图片解码器的选择。一般来讲有几种选择：</span><span style="font-size: 14pt; ">1</span><span style="font-family: 宋体; font-size: 14pt; ">、组装各种开源库，如</span><span style="font-size: 14pt; ">libpng, libjpg, giflib</span><span style="font-family: 宋体; font-size: 14pt; ">等，支持什么格式就得添加对应的解码器；</span><span style="font-size: 14pt; ">2</span><span style="font-family: 宋体; font-size: 14pt; ">、开源解码包，如</span><span style="font-size: 14pt; ">freeimage</span><span style="font-family: 宋体; font-size: 14pt; ">，没用过但听说也很不错；</span><span style="font-size: 14pt; ">3</span><span style="font-family: 宋体; font-size: 14pt; ">、</span><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">，支持图片格式广泛，接口简单，性能一般。当然还有其它方式，大抵差不多。我选择的是</span><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">，图简便好用，且目前微软支持的OS上都是自带的，无需发布？!。对</span><span style="font-size: 14pt; ">QQ</span><span style="font-family: 宋体; font-size: 14pt; ">的程序集</span><span style="font-size: 14pt; ">DLL</span><span style="font-family: 宋体; font-size: 14pt; ">进行分析，发现其中贯穿了各种解码技术，有直接采用开源库的，也有依赖</span><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">的，不知道是历史遗留问题，还是各个部门之间技术偏好，抑或是另有玄机。扯到技术偏好，让我头疼的就是程序员在项目中随意的引入库，解决同一个问题，往往看到不同的人用不同的技术，甚至同一个人在不同的项目中造不同的轮子，有时候没法说服别人，只能罢了。</span></p>  <p>&nbsp;</p>  <p><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">的初始化需要注意一件事情：不要在</span><span style="font-size: 14pt; ">DLL</span><span style="font-family: 宋体; font-size: 14pt; ">入口处加载或卸载</span><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">，否则会发生锁死现象，具体可查阅</span><span style="font-size: 14pt; ">msdn</span><span style="font-family: 宋体; font-size: 14pt; ">。</span></p>  <p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">考虑到高性能的界面中可能会采用多线程</span><span style="font-size: 14pt; ">UI</span><span style="font-family: 宋体; font-size: 14pt; ">，因此我建立了</span><span style="font-size: 14pt; ">ThreadState</span><span style="font-family: 宋体; font-size: 14pt; ">的</span><span style="font-size: 14pt; ">TLS</span><span style="font-family: 宋体; font-size: 14pt; ">对象，所有的数据都存储在这个对象中，同步线程消息是通过惯用手法之隐藏窗口来保证。时至今日，或者早</span><span style="font-size: 14pt; ">1</span><span style="font-family: 宋体; font-size: 14pt; ">、</span><span style="font-size: 14pt; ">2</span><span style="font-family: 宋体; font-size: 14pt; ">年，我才深刻理解</span><span style="font-size: 14pt; ">mfc</span><span style="font-family: 宋体; font-size: 14pt; ">库设计时的那些</span><span style="font-size: 14pt; ">state</span><span style="font-family: 宋体; font-size: 14pt; ">管理结构体的用处。</span><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">的初始化在</span><span style="font-size: 14pt; ">ThreadState</span><span style="font-family: 宋体; font-size: 14pt; ">构造函数中调用，正好避免了加载冲突这个问题。</span><span style="font-size: 14pt; ">TLS</span><span style="font-family: 宋体; font-size: 14pt; ">对象的销毁技巧来自谷歌的</span><span style="font-size: 14pt; ">chromium</span><span style="font-family: 宋体; font-size: 14pt; ">源码中的</span><span style="font-size: 14pt; ">base</span><span style="font-family: 宋体; font-size: 14pt; ">库里的</span><span style="font-size: 14pt; ">tls</span><span style="font-family: 宋体; font-size: 14pt; ">实现，原始出处来自</span><span style="font-size: 14pt; ">CodeProject</span><span style="font-family: 宋体; font-size: 14pt; ">：</span><a href="http://www.codeproject.com/threads/tls.asp"><span style="font-size: 14pt; ">http://www.codeproject.com/threads/tls.asp</span></a><span style="font-family: 宋体; font-size: 14pt; ">。嗯，其实牛逼的程序员也是纵览乾坤，吸取精华。提到</span><span style="font-size: 14pt; ">CodeProject</span><span style="font-family: 宋体; font-size: 14pt; ">，顺带提一下我的学习历程，早期疯狂的泡这个网站，几乎</span><span style="font-size: 14pt; ">VC</span><span style="font-family: 宋体; font-size: 14pt; ">方面的东西都把玩过，形成了自己的点状知识积累，类似的有</span><span style="font-size: 14pt; ">CodeGuru</span><span style="font-family: 宋体; font-size: 14pt; ">、</span><span style="font-size: 14pt; ">vckbase</span><span style="font-family: 宋体; font-size: 14pt; ">（现在已经成为广告站了）；之后是&nbsp;</span><span style="font-size: 14pt; ">sourceforge</span><span style="font-family: 宋体; font-size: 14pt; ">、</span><span style="font-size: 14pt; ">codegoogle</span><span style="font-family: 宋体; font-size: 14pt; ">、</span><span style="font-size: 14pt; ">codeplex&nbsp;</span><span style="font-family: 宋体; font-size: 14pt; ">找一些小的项目研究，形成自己的线状知识结构；再后来就是大型的源码阅读，偶尔会去谷歌讨论组、微软新闻组看一些疑难杂症问题，构成了自己的面状知识体系。每个人都有自己的学习方法论，这里仅仅是分享我自己的。很多东西都已经看上去有些过时，现在的年轻程序员可能接触的是</span><span style="font-size: 14pt; ">stackoverflow</span><span style="font-family: 宋体; font-size: 14pt; ">、</span><span style="font-size: 14pt; ">github</span><span style="font-family: 宋体; font-size: 14pt; ">等。</span></p>  <p>&nbsp;</p>  <p><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">中的</span><span style="font-size: 14pt; ">Image</span><span style="font-family: 宋体; font-size: 14pt; ">是抽象接口，为了方便使用，增加了一层简易封装</span><span style="font-size: 14pt; ">IMImage</span><span style="font-family: 宋体; font-size: 14pt; ">，主要接口如下：</span></p>  <p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">static</span>&nbsp;IMImage*&nbsp;FromFile(<span style="color: #0000FF; ">const</span>&nbsp;std::wstring&amp;&nbsp;uri);<br /><br /><span style="color: #0000FF; ">long</span>&nbsp;AddRef();<br /><span style="color: #0000FF; ">long</span>&nbsp;Release();<br /><br /><span style="color: #0000FF; ">const</span>&nbsp;std::wstring&amp;&nbsp;uri()&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;{&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;uri_;&nbsp;}<br />UINT&nbsp;frame_count()&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;{&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;frame_count_;&nbsp;}<br /><span style="color: #0000FF; ">bool</span>&nbsp;IsAnimate()&nbsp;<span style="color: #0000FF; ">const</span>&nbsp;{&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;frame_count_&nbsp;&gt;&nbsp;1;&nbsp;}<br /><span style="color: #0000FF; ">long</span>&nbsp;GetFrameDelay(UINT&nbsp;frame)&nbsp;<span style="color: #0000FF; ">const</span>;<br /><span style="color: #0000FF; ">long</span>&nbsp;GetWidth()&nbsp;<span style="color: #0000FF; ">const</span>;<br /><span style="color: #0000FF; ">long</span>&nbsp;GetHeight()&nbsp;<span style="color: #0000FF; ">const</span>;<br />Gdiplus::Image*&nbsp;GetImage(UINT&nbsp;frame);</div><span style="font-size: 14pt; "><br /></span><p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">通过</span><span style="font-size: 14pt; ">uri</span><span style="font-family: 宋体; font-size: 14pt; ">加载图片，</span><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">对本地图片的加载非常简单，</span><span style="font-size: 14pt; ">Image::FromFile</span><span style="font-family: 宋体; font-size: 14pt; ">可以直接返回；对于网络图片，需要先通过</span><span style="font-size: 14pt; ">WinINet</span><span style="font-family: 宋体; font-size: 14pt; ">下载到本地，然后再加载。</span></p>  <p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">由于使用了</span><span style="font-size: 14pt; ">tls</span><span style="font-family: 宋体; font-size: 14pt; ">技术，这里的引用计数实现很简单，就是</span><span style="font-size: 14pt; ">++</span><span style="font-family: 宋体; font-size: 14pt; ">、</span><span style="font-size: 14pt; ">-- </span><span style="font-family: 宋体; font-size: 14pt; ">操作。曾经有人告诉我引用技术是为了解决多线程中对象的生命周期问题，我欲与否认。引用计数只是为了解决对象的生命周期问题，而这种情况往往在多线程中出现，因此多线程中或多或少会用到引用计数。</span></p>  <p>&nbsp;</p>  <p><span style="font-size: 14pt; ">IMImage</span><span style="font-family: 宋体; font-size: 14pt; ">类可以简单的返回一些</span><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">的</span><span style="font-size: 14pt; ">Image</span><span style="font-family: 宋体; font-size: 14pt; ">对象提供的图片信息，而</span><span style="font-size: 14pt; ">GetImage</span><span style="font-family: 宋体; font-size: 14pt; ">需要多做一些事情。对于包含多帧图片的文件，在绘制的时候需要通过</span><span style="font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">的</span><span style="font-size: 14pt; ">Image::SelectActiveFrame</span><span style="font-family: 宋体; font-size: 14pt; ">方法选择当前帧，该操作非常耗时，因此在加载图片的时候，发现如果是多帧的，我们需要额外的</span><span style="font-size: 14pt; ">decode_image_</span><span style="font-family: 宋体; font-size: 14pt; ">来解码存放每一帧图像，这样除第一次渲染比较耗时外，后面的获取都是非常快的。</span></p>  <p>&nbsp;</p>  <span style="font-family: Calibri, sans-serif; font-size: 14pt; ">GetFrameDelay</span><span style="font-family: 宋体; font-size: 14pt; ">函数是获取某一帧之间的时间间隔，通过</span><span style="font-family: Calibri, sans-serif; font-size: 14pt; ">GDI+</span><span style="font-family: 宋体; font-size: 14pt; ">的</span><span style="font-family: Calibri, sans-serif; font-size: 14pt; ">PropertyItem</span><span style="font-family: 宋体; font-size: 14pt; ">可以很容易的获取。需要指出的是</span><span style="font-family: Calibri, sans-serif; font-size: 14pt; ">PropertyItem</span><span style="font-family: 宋体; font-size: 14pt; ">类使用起来却不是类的方式，需要手动</span><span style="font-family: Calibri, sans-serif; font-size: 14pt; ">new</span><span style="font-family: 宋体; font-size: 14pt; ">出一块内存，所以这里用一个结构体我看更合适，而且还是</span><span style="font-family: Calibri, sans-serif; font-size: 14pt; ">C</span><span style="font-family: 宋体; font-size: 14pt; ">风格的。<br /><br /></span><p><span style="font-family: 宋体; font-size: 14pt; ">再增加一个</span><span style="font-size: 14pt; ">IMImageService</span><span style="font-family: 宋体; font-size: 14pt; ">，管理整个系统中用到的</span><span style="font-size: 14pt; ">IMImage</span><span style="font-family: 宋体; font-size: 14pt; ">对象，接口很简单：</span></p>  <p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->IMImage*&nbsp;GetImage(<span style="color: #0000FF; ">const</span>&nbsp;std::wstring&amp;&nbsp;uri);<br /><span style="color: #0000FF; ">void</span>&nbsp;ReleaseImage(IMImage*&nbsp;image);</div><p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">需要图像就找它要，用完之后记得释放即可。</span></p>  <p>&nbsp;</p>  <p><span style="font-family: 宋体; font-size: 14pt; ">这样就完成了图片的管理，目前来说足够用，也非常简单，不是么？</span></p><img src ="http://www.cppblog.com/wlwlxj/aggbug/179142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-06-17 09:48 <a href="http://www.cppblog.com/wlwlxj/archive/2012/06/17/179142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>richedit研究02 – 大纲</title><link>http://www.cppblog.com/wlwlxj/archive/2012/06/14/178859.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Thu, 14 Jun 2012 14:49:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/06/14/178859.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/178859.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/06/14/178859.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/178859.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/178859.html</trackback:ping><description><![CDATA[<p><span style="font-family: 宋体; font-size: 18pt; ">自第一篇发布后，收到了不少建设性的意见，这里尤其感谢网友</span><span style="font-size: 18pt; ">weolar</span><span style="font-family: 宋体; font-size: 18pt; ">、路人甲，他们在给我重要提示的同时让我意识到自己的武断。上篇我曾经说过</span><span style="font-size: 18pt; ">richedit</span><span style="font-family: 宋体; font-size: 18pt; ">是非常底层的实现，其实我起初也不这么认为，只是在</span><span style="font-size: 18pt; ">Win2K</span><span style="font-family: 宋体; font-size: 18pt; ">泄漏代码中能找到几乎所有想要的内容同时却无法发现</span><span style="font-size: 18pt; ">richedit</span><span style="font-family: 宋体; font-size: 18pt; ">半点踪迹故形成这种判定。事实正如网友</span><span style="font-size: 18pt; ">weolar</span><span style="font-family: 宋体; font-size: 18pt; ">、路人甲提及，</span><span style="font-size: 18pt; ">richedit</span><span style="font-family: 宋体; font-size: 18pt; ">属于应用层的实现，看过代码之后感受到一种亲切感，因为大多数概念在我移植</span><span style="font-size: 18pt; ">ie</span><span style="font-family: 宋体; font-size: 18pt; ">代码的时候都曾见到过，只是当初并未深入图文编排细节，仅着眼于渲染引擎。再次感谢这两位网友，不是他们的帮助，我无法在短时间内完成一个初步的具备完美特征的</span><span style="font-size: 18pt; ">IM RichEdit</span><span style="font-family: 宋体; font-size: 18pt; ">实现。</span><span style="font-size: 18pt; ">WinNT</span><span style="font-family: 宋体; font-size: 18pt; ">中的</span><span style="font-size: 18pt; ">richedit</span><span style="font-family: 宋体; font-size: 18pt; ">应该是</span><span style="font-size: 18pt; ">1.0</span><span style="font-family: 宋体; font-size: 18pt; ">，现在普遍的是</span><span style="font-size: 18pt; ">2.0</span><span style="font-family: 宋体; font-size: 18pt; ">，最近研究发现</span><span style="font-size: 18pt; ">Win8</span><span style="font-family: 宋体; font-size: 18pt; ">中更多的暴露了</span><span style="font-size: 18pt; ">TOM</span><span style="font-family: 宋体; font-size: 18pt; ">接口，如果采用那些接口，实现起来会更加简单，这个后面我会提及。给我的感觉就是在实现一样东西的时候，谁都意识不到它会那么经用，到后来发现扩展性不够的时候，升级起来却并不是那么干脆了，顾及兼容性，只能在大版本更新的时候动大手术。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: 宋体; font-size: 18pt; ">在我实作的同时，大量使用了搜索引擎和谷歌技术论坛。我没有找到直接答案，然而各种扑朔迷离的线索总是恍恍惚惚带着我走向最终的胜利，让我再次体会到成功贵在坚持不懈。在自认为差不多满意的时候，通过跟同事的探讨，我横下心继续追求完美，相信自己做的不会比</span><span style="font-size: 18pt; ">QQ</span><span style="font-family: 宋体; font-size: 18pt; ">的实现差。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: 宋体; font-size: 18pt; ">目前完成了大纲中的高效动画实现部分，这部分耗时</span><span style="font-size: 18pt; ">1</span><span style="font-family: 宋体; font-size: 18pt; ">周，另外加上前</span><span style="font-size: 18pt; ">2</span><span style="font-family: 宋体; font-size: 18pt; ">周的工作之外时间的技术调用。接口基于</span><span style="font-size: 18pt; ">IRichEditOle</span><span style="font-family: 宋体; font-size: 18pt; ">，也就是说只要符合</span><span style="font-size: 18pt; ">RichEdit</span><span style="font-family: 宋体; font-size: 18pt; ">实现规范的都支持，包括窗口的和无窗口的。那什么是符合</span><span style="font-size: 18pt; ">RichEdit</span><span style="font-family: 宋体; font-size: 18pt; ">实现规范呢，就是符合</span><span style="font-size: 18pt; ">RichEdit</span><span style="font-family: 宋体; font-size: 18pt; ">窗口实现的方式，特指无窗口的</span><span style="font-size: 18pt; ">RichEdit</span><span style="font-family: 宋体; font-size: 18pt; ">实现。你可能说我不知道如何实现无窗口的</span><span style="font-size: 18pt; ">RichEdit</span><span style="font-family: 宋体; font-size: 18pt; ">，这个不要紧，我以后会做。后面的文章我会陆续把第一部分实现进行详细讲解，同时实现第二部分。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: 宋体; font-size: 18pt; ">效果图：</span></p>
<p>&nbsp;<img src="http://www.cppblog.com/images/cppblog_com/wlwlxj/8028/o_demo.png" alt="" /></p>
<span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;"><a href="http://www.cppblog.com/files/wlwlxj/im.rar"><span style="font-size: 18pt; ">示例工程下载</span></a><br />
<br />
</span><span style="font-family: 宋体; font-size: 18pt; "><br /><br />大纲如下：</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;"><br />
</span>
<h1><span style="font-family:宋体;">高效动画实现</span></h1>
<h2><span style="font-family:宋体;">高效图片管理</span></h2>
<h2><span style="font-family:宋体;">高效时钟</span></h2>
<h2><span style="font-family:宋体;">动画控件</span></h2>
<h2><span style="font-family:宋体;">高效动画刷新</span></h2>
<h1><span style="font-family:宋体;">拖拽剪切粘贴支持</span></h1>
<h2>Process Monitor</h2>
<h2>QQ<span style="font-family:宋体;">动画组件分析</span></h2>
<h2>ClipSpy</h2>
<h2>QQ<span style="font-family:宋体;">的剪切板格式分析</span></h2>
<h2>IDataObject</h2>
<h2>Xml<span style="font-family:宋体;">序列化反序列化</span></h2>
<h2><span style="font-family:宋体;">拖拽</span></h2>
<h2><span style="font-family:宋体;">粘贴</span></h2>
<h1>OLE<span style="font-family:宋体;">扩展</span></h1>
<h2><span style="font-family:宋体;">仿</span>QQ<span style="font-family:宋体;">新闻摘要<br />
...</span></h2><img src ="http://www.cppblog.com/wlwlxj/aggbug/178859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-06-14 22:49 <a href="http://www.cppblog.com/wlwlxj/archive/2012/06/14/178859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>richedit研究开篇01</title><link>http://www.cppblog.com/wlwlxj/archive/2012/05/20/175489.html</link><dc:creator>万连文</dc:creator><author>万连文</author><pubDate>Sun, 20 May 2012 12:01:00 GMT</pubDate><guid>http://www.cppblog.com/wlwlxj/archive/2012/05/20/175489.html</guid><wfw:comment>http://www.cppblog.com/wlwlxj/comments/175489.html</wfw:comment><comments>http://www.cppblog.com/wlwlxj/archive/2012/05/20/175489.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/wlwlxj/comments/commentRss/175489.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wlwlxj/services/trackbacks/175489.html</trackback:ping><description><![CDATA[<br /><h1><span style="font-family:宋体;">研究背景</span></h1>  <p><span style="font-family:宋体;">自职业生涯起到现在，我参与过三个</span>im<span style="font-family:宋体;">类产品，其中我主要负责的是界面库开发。众所周知，</span>im<span style="font-family:宋体;">软件中有一个非常重要的控件用于消息展示，实现方式无外乎基于</span>richedit<span style="font-family:宋体;">（目前这一类为主要方式，代表：</span>QQ<span style="font-family:宋体;">）、基于</span>webbrowser<span style="font-family:宋体;">（代表：</span>GTalk<span style="font-family:宋体;">），可能会有基于</span>WebKit<span style="font-family:宋体;">的，我没有刻意去搜集。很多时候对于相对简单的情况，比如在游戏中，完全可以自己绘制。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">我曾经模仿过</span>GTalk<span style="font-family:宋体;">的实现方式，由于当时自己经验欠缺以及与公司写页面的人沟通上的问题，效果不是那么满意，仅仅够用，后来自己离开也没再继续做这方面研究。机制上这种方式是可行的，</span>native<span style="font-family:宋体;">端事情不多。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">后来的工作中，主要是基于</span>richedit<span style="font-family:宋体;">在做，都是安排其他人负责。在开发过程中，遇到种种问题，用过各种不优雅的&#8220;伎俩&#8221;，由于欠缺</span>OLE<span style="font-family:宋体;">知识，做的人很痛苦，找不到乐趣。微软官方有一个例子，然而只披露了使用技巧的冰山一角；</span>codeproject<span style="font-family:宋体;">也有少的可怜的几个例子，经不住商业化应用；互联网搜索的一些文章大多都是简单的插入图片等，聊胜于无。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">《基于</span>Chrome<span style="font-family:宋体;">开源提取的界面开发框架》系列文章获得了不少支持，在提取过程中我自己也成长很多。抽取出来的引擎要想用于商业化开发，我个人觉得欠缺的主要是富文本渲染这一块，这使我开始研究</span>richedit<span style="font-family:宋体;">。断断续续，期间各种事情，几经放弃。后来一个网友在这个问题上又找到我，临时的帮助他解决问题之后，不禁感叹，为什么互联网上找不到一个优雅的解决方案甚至是深入的介绍？</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">网友</span>megax<span style="font-family:宋体;">的文章</span><a href="http://www.cppblog.com/megax/archive/2012/03/22/168601.html">http://www.cppblog.com/megax/archive/2012/03/22/168601.html</a><span style="font-family:宋体;">中关于制作编辑器方面知识提及的</span><a href="http://www.catch22.net/tuts/neatpad">http://www.catch22.net/tuts/neatpad</a><span style="font-family:宋体;">让我着实佩服，老外对待研究的态度真的很严谨，分享的开发性和持续性方面远远胜于我们。系列文章中的绝大多数概念知识我都接触过，然而很多没有深究，经验远远不如文章主人。</span></p>  <p>&nbsp;</p>  <p>Richedit<span style="font-family: 宋体;">的研究的大部分知识都在</span>OLE<span style="font-family:宋体;">方面。现在计算机的发展，技术的百花齐放，使得</span>Windows<span style="font-family:宋体;">平台不再那么大行其道，</span>Windows<span style="font-family:宋体;">技术也不再那么不可一世，</span>Mfc<span style="font-family:宋体;">越来越被抛弃，</span>Windows<span style="font-family:宋体;">程序员诚惶诚恐，新生代早早把自己定位在更炫更酷更激情的技术平台。在</span>Windows Native<span style="font-family:宋体;">开发没有彻底失宠前，我打算把自己死啃得来的</span>OLE<span style="font-family:宋体;">知识发挥&#8220;余热&#8221;，对</span>richedit<span style="font-family:宋体;">这个东西应用于</span>im<span style="font-family:宋体;">领域的问题解决一下，希望对其他人有帮助，也希望没有重复造轮子。</span></p>  <h1><span style="font-family:宋体;"><br /><br />研究目标</span></h1>  <p>Richedit<span style="font-family: 宋体;">是</span>Windows<span style="font-family:宋体;">底层的组件，甚至在</span>2004<span style="font-family:宋体;">年泄漏的</span>Win2K<span style="font-family:宋体;">代码中都没有，它是独立于</span>edit<span style="font-family:宋体;">组件的，而</span>edit<span style="font-family:宋体;">位于</span>ntuser<span style="font-family:宋体;">中，亦相当底层，虽在泄漏代码中出现，然而抽取出来的可能性不大。从某种角度来讲，</span>Reactos <span style="font-family:宋体;">就是抄袭的这份代码，明眼人可以从其死灰复燃的更新列表中发觉。很奇怪的是</span>Reactos <span style="font-family:宋体;">的代码中有</span>richedit<span style="font-family:宋体;">，我也移植过，只是后来发现功能实在太弱，无可用性，遂放弃，至于</span>Wine <span style="font-family:宋体;">是不是抄袭这份代码，我无从得知，也没精力再去跟踪。</span></p>  <p>&nbsp;</p>  <p>Richedit <span style="font-family: 宋体;">的接口相当稳定，我在</span>Win8<span style="font-family:宋体;">中试验过完全兼容，我想它应该会持续很久，所以值得去好好研究一把。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">《基于</span>Chrome<span style="font-family:宋体;">开源提取的界面开发框架》的</span>view<span style="font-family:宋体;">框架，如果能有一个</span>rtf<span style="font-family:宋体;">格式的渲染利器，配以</span>ole<span style="font-family:宋体;">的展示，我想足以成为互联网商业开发的</span>UI<span style="font-family:宋体;">解决方案。</span></p>  <p>&nbsp;</p>  <p>Richedit <span style="font-family: 宋体;">就机制上来讲跟</span>WebKit<span style="font-family:宋体;">一样，或者应该反过来说。</span>Richedit<span style="font-family:宋体;">窗口本身是对</span>ITextServices<span style="font-family:宋体;">的封装，实现</span>ITextHost<span style="font-family:宋体;">接口与</span>ITextServices<span style="font-family:宋体;">交互提供平台支持。</span>ITextServices<span style="font-family:宋体;">的支持分两大类：基于文本的</span>ITextDocument<span style="font-family:宋体;">和基于</span>ole<span style="font-family:宋体;">的</span>IRichEditOle<span style="font-family:宋体;">。作为</span>ole<span style="font-family:宋体;">容器，提供的功能主要通过实现几个接口完成的，包括：</span>IOleClientSite<span style="font-family:宋体;">、</span>IAdviseSink<span style="font-family:宋体;">、</span>IOleInPlaceSite<span style="font-family:宋体;">，缺省的</span>Richedit <span style="font-family:宋体;">貌似不支持定位激活，想要达到激活效果必须支持最后一个接口。与剪贴板和拖拽数据打交道需要支持统一数据传输接口</span>IDataObject<span style="font-family:宋体;">。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">研究主要参考对象为</span>QQ<span style="font-family:宋体;">，目标为：支持粘贴格式、动画</span>ole<span style="font-family:宋体;">控件、定制</span>ole<span style="font-family:宋体;">菜单、拖拽、文本操纵、窗口</span>/<span style="font-family:宋体;">无窗口的统一支持等，我会在下一篇列出详细的大纲。</span></p>  <h1><span style="font-family:宋体;"><br /><br />已做工作</span></h1>  <p>OLE<span style="font-family:宋体;">标准提供了大量工业化标准接口以及繁杂的交互规范，事实上除了微软（以</span>office<span style="font-family:宋体;">系列产品为典范），鲜有软件全盘实施。可以说</span>MFC<span style="font-family:宋体;">的大部分工作都是用在实现</span>OLE<span style="font-family:宋体;">，所以其臃肿大抵源于此，不得不臃肿，早些年我接触</span>wtl<span style="font-family:宋体;">后得出，</span>MFC - WTL == OLE<span style="font-family:宋体;">，或许还有一些打印等方面设施，但我不觉得这些是主要特性。以我现在眼光来看待，把</span>MFC<span style="font-family:宋体;">拆分开，其集合类、打印框架、</span>COM<span style="font-family:宋体;">支持、</span>OLE<span style="font-family:宋体;">支持、文档</span>/<span style="font-family:宋体;">视图</span>/<span style="font-family:宋体;">框架模板（尽管现在用的极少，大多在行业软件领域）、进程</span>/<span style="font-family:宋体;">线程</span>/<span style="font-family:宋体;">模块状态管理等，都还不错，呃</span>&#8230;<span style="font-family:宋体;">，貌似我快把它说全了，不好意思，我的老毛病又犯了，其实在心底，我还有那么一点不舍，即便我很多年没有用它开发商业软件了。我从</span>MFC<span style="font-family:宋体;">学到的东西太多太多，以至于我不肯说它的坏话。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">再一次，我选择了从</span>MFC<span style="font-family:宋体;">中抽取代码。我尝试过直接使用</span>MFC<span style="font-family:宋体;">，静态链接以便偷偷的不告诉别人，以免&#8220;破坏形象&#8221;。但我不觉得我能做到，原因就是整个</span>MFC<span style="font-family:宋体;">耦合的比较紧密，我不想或者无法使用整个框架来对外提供服务，所以我不得不制造小轮子。我也试过直接实现，但是工作量还真不小，我怕自己等不及，故再次祭出看家本领，顺藤摸瓜，牵出一个支持</span>OLE<span style="font-family:宋体;">的最小内核，经过</span>1-2<span style="font-family:宋体;">周我能腾出的时间，终于做到了，于是我写下了开篇，后面我需要把整个思路刻画出来。下面是一个示例，插入浏览器，支持定位激活：</span></p>  <p><img src="http://www.cppblog.com/images/cppblog_com/wlwlxj/8028/o_richedit_inplace_active.png" width="1106" height="487" alt="" /><br /></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">这个例子不具备任何说明性，只是用来测试最小的</span>OLE<span style="font-family:宋体;">内核框架是否可以工作。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">测试的接口如下：</span></p>  <p><img src="http://www.cppblog.com/images/cppblog_com/wlwlxj/8028/o_richedit_interface.png" alt="" /><br /></p>  <p>&nbsp;</p>  <p><span style="font-family: 宋体; ">这一篇到此为止！依然是闲话多，干货少。我会努力的！</span></p><img src ="http://www.cppblog.com/wlwlxj/aggbug/175489.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wlwlxj/" target="_blank">万连文</a> 2012-05-20 20:01 <a href="http://www.cppblog.com/wlwlxj/archive/2012/05/20/175489.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>