??xml version="1.0" encoding="utf-8" standalone="yes"?>欧美国产成人久久精品,精品99久久aaa一级毛片,99久久久国产精品免费无卡顿http://www.shnenglu.com/mydriverc/category/4807.html如果想飞得高Q就该把地^U忘?/description>zh-cnMon, 19 May 2008 13:37:05 GMTMon, 19 May 2008 13:37:05 GMT60COM lg设计与应用(一Q?---重读一?别有一番感?/title><link>http://www.shnenglu.com/mydriverc/articles/29061.html</link><dc:creator>旅?/dc:creator><author>旅?/author><pubDate>Tue, 31 Jul 2007 02:20:00 GMT</pubDate><guid>http://www.shnenglu.com/mydriverc/articles/29061.html</guid><wfw:comment>http://www.shnenglu.com/mydriverc/comments/29061.html</wfw:comment><comments>http://www.shnenglu.com/mydriverc/articles/29061.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/mydriverc/comments/commentRss/29061.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/mydriverc/services/trackbacks/29061.html</trackback:ping><description><![CDATA[ <p> <b>一、前a</b> <br /> <br />  公元一?ji)?ji)五年某个夜黑风高的晚上,我的一位老师跟我_“小杨呀Q以后写E序和搭积木一样啦。你赶快学习一些OLE的技术吧......”,当时我心里就L?Q“开什么玩W?搭积木方式写E序Q再q?00q吧......”,但作Z名听话的好学生,我开始在书店里“踅摸”(?Q有关OLE的书c(?Q。功夫不负有心hQ终于买C我的W一本COM书《OLE2 高~程技术》,q本800多页的大布头p了我1/5的月工资呀......于是开始日夜耕读.....<br />    功夫不负有心人,我坚持读完了全部著作Q感xQ这本书Q在说什么呐Q?br />    功夫不负有心人,我又d了一遍大布头Q感xQ咳~~~Q没懂!<br />    功夫不负有心人,我再Q我?我再?... 感想是:哦~~~Q读懂了一点点啦,哈哈哈?br />    ......  ......<br />    功夫不负有心人,我终于,我终于懂了?br />    800늚书对现在的我来说Q其实也?0几页有用。到q时候才体会Z么叫“书读薄”的道理了。到后来Q能买到的书也多了,上网也更方便更便宜了......<br /><br />  Z让VCKBASE上的朋友Q不再经历我曄的痛苦、不再重y我“无头苍蝇”般探烦的艰辛、ؓ了VCKBASE的蓬勃发展、ؓ了中国Y件事业的NQ糟p,吹的太也高了Q?.....我打节U一些在 BBS 上赚分的旉Q写个系列论文,叫“COMlg设计与应用”吧。今天是W一部分——v源?br /><br /><b>二、文件的存储</b><br /><br />  传说350q前Q牛被Ҏ(gu)砸到了头Q于是发C万有引力。但C二十一世纪的现在,M一个技术的发明和发展,已经不再依靠圣h灵光的一闪。技术的q步转而是被社会的需求、商业的利益、竞争的压力、行业的渗透等推动的。微软在Windowsq_上的lg技术也不例外,它的发明Q有其必然因素。什么是q个因素那?{案是——文件的存储?br />  打开C本程序,输入了一文章后Q保存。——这L文g叫“非l构化文件”;<br />  打开?sh)子表格E序Q输入一个班的学生姓名和考试成WQ保存。——这L文g叫“标准结构化文g”;<br />  在我们写的程序中Q需要把特定的数据按照一定的l构和顺序写到文件中保存。——这L文g叫“自定义l构化文件”;Q比?*.bmp 文gQ?br />  以上三种cd的文Ӟ大家都见的多了。那么文件存储就依靠上述的方式能满所有的应用需求吗Q恩~~~Q至从计算机发明后?0多年来,一直是够用的了。嘿嘿,下面看看商业利益的推动作用,Ҏ(gu)?的存储Ş式生了什么变化吧?0岁以上的朋友Q我估计以前都用过以下几个著名的YӞWordStarQ独霸DOS下的英文~辑软gQ,WPSQ裘伯君写的中文~辑软gQ据说当q的市场占有率高?0%Q各U计机培训班的必修评Q,LOTUS-123Q莲花公司出品的?sh)子表格软gQ?.....<br />    微Y在成功地推出 Windows 3.1 后,开始垂涎桌面办公自动化软g领域。微软的 OFFICE 开发部门,各小l分别独立地开发了 WORD ?EXCEL {YӞq用“自定义l构”方式,Ҏ(gu)件进行存储。在Ȁ烈的市场竞争下,Z打|竞争Ҏ(gu)Q微软自然地产生了一个念?-----如果我能?WORD E序中嵌?EXCELQ那么用户在购买了我 WORD 软g的情况下Q不没有必要再?LOTUS-123 了吗Q!“恶毒”(中国微Y的同志们看到了这个词Q不要激动,我是加了引号的呀Q的计划产生后,他们开始了实施工作Q这是 COM 的前w?OLE 的v源(?Q。但立刻遇C一个严重的技术问题:需要把 WORD 产生?DOC 文g?EXCEL 产生?XLS 文g保存在一赗?br />  </p> <table cellspacing="1" width="100%" border="1"> <tbody> <tr> <td width="33%"> <p align="center">Ҏ(gu)</p> </td> <td width="31%"> <p align="center">优点</p> </td> <td width="103%"> <p align="center">~点</p> </td> </tr> <tr> <td width="33%">建立一个子目录Q把 DOC、XLS 存储在这同一个子目录中?/td> <td width="31%">数据隔离性好QWORD 不用了解 EXCEL 的存储结构;Ҏ(gu)扩展?/td> <td width="103%">l构太松散,Ҏ(gu)造成数据的损坏或丢失?br />不易携带?/td> </tr> <tr> <td width="33%">修改文g存储l构Q在DOCl构基础上扩展出包容 XLS 的结构?/td> <td width="31%">l构紧密Q容易携带和l一理?/td> <td width="103%">WORD 的开发h员需要通晓 EXCEL 的存储格式;~少扩展性,M能新加一个类型就扩展一下结构吧Q!</td> </tr> </tbody> </table> <p>    以上两个Ҏ(gu)Q都有严重的~陷Q怎么解决那?如果能有一个新Ҏ(gu)Q能够合q前两个Ҏ(gu)的优点,消灭~点Q该多好呀......微Y是作盘***作系lv家的Q于是很自然C们提Z一个非常完的设计Ҏ(gu)Q那是把磁盘文件的理方式UL到文件中?-----复合文gQ俗U“文件中的文件系l”。连微Y当年都没有想刎ͼp么一个简单的xQ居然最后就演变Z COM lgE序设计的方法。可以说Q复合文件是 COM 的基矟뀂下图是盘文gl织方式与复合文件组l方式的cL图:<br /><img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.vckbase.com/document/journal/vckbase43/images/stmtutpic1.jpg" onload="javascript:if(this.width>500){this.resized=true;this.style.width=500;}" border="0" /><br />图一、左侧表CZ个磁盘下的文件组l方式,右侧表示一个复合文件内部的数据l织方式?br /><br /><b>三、复合文件的特点</b></p> <ol> <li>复合文g的内部是使用指针构造的一|q行理的。编写程序的时候要注意Q由于用的是单向指针,因此当做定位***作的时候,向后定位比向前定位要快; </li> <li>复合文g中的“流对象”,是真正保存数据的I间。它的存储单位ؓ512字节。也是_即你在中只保存了一个字节的数据Q它也要占据512字节的文件空间。啊~~~Q这也太费了呀Q不费Q因为文件保存在盘上,即一个字节也q要占用一个“簇”的I间那; </li> <li>不同的进E,或同一个进E的不同U程可以同时讉K一个复合文件的不同部分而互不干扎ͼ </li> <li>大家都有q样的体会,当需要往一个文件中插入一个字节的话,需要对整个文gq行***作,非常烦琐q且效率低下。而复合文件则提供了非常方便的“增量访问”能力; </li> <li>当频J地删除文gQ复制文件后Q磁盘空间会变的很零,需要用磁盘整理工兯行重新整合。和盘理非常怼Q复合文件也会生这个问题,在适当的时候也需要整理,但比较简单,只要调用一个函数就可以完成了?</li> </ol> <p> <b>四、浏览复合文?/b> <br /> <br />  VC6.0 附带了一个工兯Y件“复合文件浏览器”,文g名是“vc目录\Common\Tools\<strong>DFView.exe</strong>”。ؓ了方便用该E序Q可以把它加到工?tools)菜单中。方法是QTools\Customize...\Tools卡片中增加新的项目。运?DFView.exeQ就可以打开一个复合文件进行观察了Q注4Q。但奇怪的是,?Microsoft Visual Studio .NET 2003 中,我反而找不到q个工具E序?汗!不过q恰好提供给大家一个练习的ZQ在你阅d本篇文章q掌握了~程Ҏ(gu)后,自己写一个“复合文件浏览编辑器”程序,又练手了Q还有实用的价倹{?br /><br /><b>?/b><b>、复合文件函?/b><br /><br />  复合文g的函数和盘目录文g?**作非常类伹{所有这些函敎ͼ被分?U类型:WIN API 全局函数Q存?IStorage 接口函数Q流 IStream 接口函数。什么是接口Q什么是接口函数Q以后的文章中再陆箋介绍Q这里大家只要把“接口”看成是完成一l相?**作功能的函数集合可以了?br />  </p> <table cellspacing="1" width="100%" border="1"> <tbody> <tr> <td width="17%"> <p align="center"> <b>WIN API 函数</b> </p> </td> <td width="46%"> <p align="center"> <b>功能说明</b> </p> </td> </tr> <tr> <td width="16%">StgCreateDocfile()</td> <td width="47%">建立一个复合文Ӟ得到根存储对?/td> </tr> <tr> <td width="16%">StgOpenStorage()</td> <td width="47%">打开一个复合文Ӟ得到根存储对?/td> </tr> <tr> <td width="16%">StgIsStorageFile()</td> <td width="46%">判断一个文件是否是复合文g</td> </tr> <tr> <td width="100%" colspan="2"> <p align="center"> </p> </td> </tr> <tr> <td width="16%"> <p align="center"> <b>IStorage 函数</b> </p> </td> <td width="46%"> <p align="center"> <b>功能说明</b> </p> </td> </tr> <tr> <td width="16%">CreateStorage()</td> <td width="46%">在当前存储中建立新存储,得到子存储对?/td> </tr> <tr> <td width="16%">CreateStream()</td> <td width="46%">在当前存储中建立新流Q得到流对象</td> </tr> <tr> <td width="16%">OpenStorage()</td> <td width="46%">打开子存储,得到子存储对?/td> </tr> <tr> <td width="16%">OpenStream()</td> <td width="46%">打开,得到对?/td> </tr> <tr> <td width="16%">CopyTo()</td> <td width="46%">复制存储下的所有对象到目标存储中,该函数可以实现“整理文Ӟ释放片I间”的功能</td> </tr> <tr> <td width="16%">MoveElementTo()</td> <td width="46%">Ud对象到目标存储中</td> </tr> <tr> <td width="16%">DestoryElement()</td> <td width="46%">删除对象</td> </tr> <tr> <td width="16%">RenameElement()</td> <td width="46%">重命名对?/td> </tr> <tr> <td width="16%">EnumElements()</td> <td width="46%">枚D当前存储中所有的对象</td> </tr> <tr> <td width="16%">SetElementTimes()</td> <td width="46%">修改对象的时?/td> </tr> <tr> <td width="16%">SetClass()</td> <td width="46%">在当前存储中建立一个特D的对象,用来保存CLSIDQ注5Q?/td> </tr> <tr> <td width="16%">Stat()</td> <td width="46%">取得当前存储中的pȝ信息</td> </tr> <tr> <td width="16%">Release()</td> <td width="46%">关闭存储对象</td> </tr> <tr> <td width="62%" colspan="2"> </td> </tr> <tr> <td width="16%"> <p align="center"> <b>IStream 函数</b> </p> </td> <td width="46%"> <p align="center"> <b>功能说明</b> </p> </td> </tr> <tr> <td width="16%">Read()</td> <td width="46%">从流中读取数?/td> </tr> <tr> <td width="16%">Write()</td> <td width="46%">向流中写入数?/td> </tr> <tr> <td width="16%">Seek()</td> <td width="46%">定位d位置</td> </tr> <tr> <td width="16%">SetSize()</td> <td width="46%">讄尺寸。如果预先知道大,那么先调用这个函敎ͼ可以提高性能</td> </tr> <tr> <td width="16%">CopyTo()</td> <td width="46%">复制数据到另一个流对象?/td> </tr> <tr> <td width="16%">Stat()</td> <td width="46%">取得当前中的系l信?/td> </tr> <tr> <td width="16%">Clone()</td> <td width="46%">克隆一个流对象Q方便程序中的不同模?**作同一个流对象</td> </tr> <tr> <td width="16%">Release()</td> <td width="46%">关闭对?/td> </tr> <tr> <td width="62%" colspan="2"> </td> </tr> <tr> <td align="middle" width="16%"> <b>WIN API 补充函数</b> </td> <td align="middle" width="46%"> <b>功能说明</b> </td> </tr> <tr> <td width="16%">WriteClassStg()</td> <td width="46%">写CLSID到存储中Q同IStorage::SetClass()</td> </tr> <tr> <td width="16%">ReadClassStg()</td> <td width="46%">dWriteClassStg()写入的CLSIDQ相当于化调用IStorage::Stat()</td> </tr> <tr> <td width="16%">WriteClassStm()</td> <td width="46%">写CLSID到流的开始位|?/td> </tr> <tr> <td width="16%">ReadClassStm()</td> <td width="46%">dWriteClassStm()写入的CLSID</td> </tr> <tr> <td width="16%">WriteFmtUserTypeStg()</td> <td width="46%">写入用户指定的剪贴板格式和名U到存储?/td> </tr> <tr> <td width="16%">ReadFmtUserTypeStg()</td> <td width="46%">dWriteFmtUserTypeStg()写入的信息。方便应用程序快速判断是否是它需要的格式数据?/td> </tr> <tr> <td width="16%">CreateStreamOnHGlobal()</td> <td width="46%">内存句柄 HGLOBAL 转换为流对象</td> </tr> <tr> <td width="16%">GetHGlobalFromStream()</td> <td width="46%">取得CreateStreamOnHGlobal()调用中用的内存句柄</td> </tr> </tbody> </table> <p>Z让大家快速地览和掌握基本方法,上面所列表的函数ƈ不是全部Q我省略了“事务”函数和未实现函数部分。更全面的介l,请阅?MSDN?br />    下面E序片段Q演CZ一些基本函数功能和调用Ҏ(gu)。?<br />CZ一Q徏立一个复合文Ӟq在其下建立一个子存储Q在该子存储中再建立一个流Q写入数据?/p> <pre>void SampleCreateDoc() { ::CoInitialize(NULL); // COM 初始? // 如果是MFCE序Q可以用AfxOleInit()替代 HRESULT hr; // 函数执行q回? IStorage *pStg = NULL; // 根存储接口指? IStorage *pSub = NULL; // 子存储接口指? IStream *pStm = NULL; // 接口指? hr = ::StgCreateDocfile( // 建立复合文g L"c:\\a.stg", // 文g名称 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, // 打开方式 0, // 保留参数 &pStg); // 取得根存储接口指? ASSERT( SUCCEEDED(hr) ); // ZH出重点Q简化程序结构,所以用了断言? // 在实际的E序中则要用条件判断和异常处理 hr = pStg->CreateStorage( // 建立子存? L"SubStg", // 子存储名U? STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0,0, &pSub); // 取得子存储接口指? ASSERT( SUCCEEDED(hr) ); hr = pSub->CreateStream( // 建立? L"Stm", // 名U? STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0,0, &pStm); // 取得接口指? ASSERT( SUCCEEDED(hr) ); hr = pStm->Write( // 向流中写入数? "Hello", // 数据地址 5, // 字节长度(注意Q没有写入字W串l尾的\0) NULL); // 不需要得到实际写入的字节长度 ASSERT( SUCCEEDED(hr) ); if( pStm ) pStm->Release();// 释放指? if( pSub ) pSub->Release();// 释放子存储指? if( pStg ) pStg->Release();// 释放根存储指? ::CoUninitialize() // COM 释放 // 如果使用 AfxOleInit(),则不调用该函? }</pre> <img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.vckbase.com/document/journal/vckbase43/images/stmtutpic2.jpg" onload="javascript:if(this.width>500){this.resized=true;this.style.width=500;}" border="0" /> <br />图二、运行示例程序一后,使用 DFView.exe 打开观察复合文g的效果图<br /><br />CZ二:打开一个复合文Ӟ<strong>枚D其根存储下的所有对?/strong>?br />---(<strong>自己的理?是不是每个对象就是一个子存储??)</strong><pre>Qi nclude <atlconv.h> // ANSI、MBCS、UNICODE 转换 void SampleEnum() { // 假设你已l做q?COM 初始化了 LPCTSTR lpFileName = _T( "c:\\a.stg" ); HRESULT hr; IStorage *pStg = NULL; USES_CONVERSION; // Q注6Q? LPCOLESTR lpwFileName = T2COLE( lpFileName ); // 转换Tcd为宽字符 hr = ::StgIsStorageFile( lpwFileName ); // 是复合文件吗Q? if( FAILED(hr) ) return; hr = ::StgOpenStorage( // 打开复合文g lpwFileName, // 文g名称 NULL, STGM_READ | STGM_SHARE_DENY_WRITE, 0, 0, &pStg); // 得到根存储接口指? IEnumSTATSTG *pEnum=NULL; // 枚D? hr = pStg->EnumElements( 0, NULL, 0, &pEnum ); ASSERT( SUCCEEDED(hr) ); STATSTG statstg; while( NOERROR == pEnum->Next( 1, &statstg, NULL) ) { // statstg.type 保存着对象cd STGTY_STREAM ?STGTY_STORAGE // statstg.pwcsName 保存着对象名称 // ...... q有旉Q长度等很多信息。请查看 MSDN ::CoTaskMemFree( statstg.pwcsName ); // 释放名称所使用的内存(?Q? } if( pEnum ) pEnum->Release(); if( pStg ) pStg->Release(); }</atlconv.h></pre><b>六、小l?/b><br /><br />  <strong>复合文gQ结构化存储Q是微Ylg思想的v源,在此基础上l发展出了持l性、命名、ActiveX、对象嵌入、现场激z?.....一pd的新技术、新概念</strong>。因此理?atlconv.h>?atlconv.h>掌握 复合文g是非帔R要的Q即使在你的E序中ƈ没有全面使用lg技术,复合文g技术也是可以单独被应用的。祝大家学习快乐QؓC会M软g事业而奋?-)<br /><br />留作业啦......<br />作业1Q写个小应用E序Q从 MSWORD ?doc 文g中,提取出附加信息(作者、公?.....Q?br />作业2Q写个全功能的“复合文件浏览编辑器”?br /><br />?Q踅?xuemo)Q动词,北方方言Q寻找搜索的意思?br />?Q问Qؓ什么不上网查资料学习?<br />     {:开什么国际玩W!在那遥远?995q代Q我?00块工资,不吃不喝正好够上100时的Internet|?br />?QOLEQ对象的q接与嵌入?br />?Q可以用 DFView.exe 打开 MSWORD ?DOC 文gq行复合文g的浏览。但是该E序q没有实现国际化Q不能打开中文文g名的复合文gQ因此需要改名后才能览?br />?QCLSIDQ在后箋的文章中介绍?br />?Q关?COM 中内存用的问题Q在后箋的文章中介绍?</atlconv.h></atlconv.h><img src ="http://www.shnenglu.com/mydriverc/aggbug/29061.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/mydriverc/" target="_blank">旅?/a> 2007-07-31 10:20 <a href="http://www.shnenglu.com/mydriverc/articles/29061.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>l构或大内存块打包的办法http://www.shnenglu.com/mydriverc/articles/29027.html旅?/dc:creator>旅?/author>Mon, 30 Jul 2007 08:51:00 GMThttp://www.shnenglu.com/mydriverc/articles/29027.htmlhttp://www.shnenglu.com/mydriverc/comments/29027.htmlhttp://www.shnenglu.com/mydriverc/articles/29027.html#Feedback0http://www.shnenglu.com/mydriverc/comments/commentRss/29027.htmlhttp://www.shnenglu.com/mydriverc/services/trackbacks/29027.htmll构或大内存块打包的办法

Revision History:

Version Date Creator Description
 

Implementation ScopeQ?/p>

l箋阅读之前Q我们假设?zhn)熟(zhn)以下知识Q?/p>

  • SAFEARRAY
  • ISTREAM
  • Microsoft MSMQ

目录Q?/p>

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream打包传递数据到MSMQ队列

1.概述

通常我们通过MSMQ传递基于XML的字W串Q但有时候也需要传递一些结构或者一些接口指针,那么如何打包传递呢Q?/p>

q实际上可以转换Z个普适问题:

如何把一个结构体(structure object)或者巨大内存块Q比?MB左右Q打包ؓPROPVARIANT-compatible的类型?

 

首先QIMSMQMessagePtr的Body属性接收_variant_t参数:

inline void IMSMQMessage::PutBody ( const _variant_t & pvarBody ) {

    HRESULT _hr = put_Body(pvarBody);

    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));

}

如果我们xl构作ؓ消息的Body写入MSMQ消息队列Q我们需要把我们的结构、大内存块或接口指针转换为_variant_t?/p>

2.借用SAFEARRAY打包把结构写入MSMQ队列

把一个结构体打包为PROPVARIANT-compatible的类型,需要用到SAFEARRAYQ一个带有边界信息的数组。这是一个常用技巧,很多文章都有提及Q我׃多解释了?/p>

但是Q注意这U方式一ơ只能打?5536字节以下的数据,q是׃

SAFEARRAY* SafeArrayCreateVector(

  VARTYPE      vt,            

  long          lLbound,          

  unsigned int  cElements);

的定义所限制的?/p>

我们通常会用SafeArrayCreateVector API创徏一个单lSAFEARRAYQ分配一个sizeof(_DATA)大小的连l内存块Q而这个函数的W三个参数是一个unsigned intcdQ所以最大值就只能?5536了?/p>

更多SAFEARRAY知识Q参见用SAFEARRAY传递对象?/p>

 
2.借用SAFEARRAY打包把结构写入MSMQ队列

l上1.1的打包步骤QVC++代码Q:

// ChangeStruct2Var函数的定义:

// W一个参敎ͼ

//   cdQCComVariant

//   作用Q接收?/p>

// W二个参敎ͼ

//   cdQ_DATA*

//   作用Q源

HRESULT ChangeStruct2Variant (CComVariant &var, _DATA *pData)

{

HRESULT hr = S_OK;

 

// 使用SafeArrayCreateVector API创徏一个单lSAFEARRAYQ分配一个sizeof(_DATA)大小的连l内存块

// VT--UI1代表非负整Ş的变量类型,1个字?/p>

// 常数'0'定义数组的下?/p>

LPSAFEARRAY lpsa = SafeArrayCreateVector(VT_UI1, 0, sizeof(_DATA));

LPBYTE pbData = NULL;

 

if (lpsa)

{

     //在你讉KSAFEARRAY数据之前Q你必须调用SafeArrayAccessData。该函数锁定数据q且q回一个指针。在q里Q锁定数l意味着增加该数l的内部计数器(cLocksQ?/p>

    hr = SafeArrayAccessData(lpsa, (void **)&pbData);

}

else

    hr = HRESULT_FROM_WIN32(GetLastError());

 

if (SUCCEEDED(hr))

{

     // 使用safe arrayQ?/p>

     // 传入的_DATA指针指向的内存复制到pbData

    CopyMemory(pbData, pData, sizeof(*pData));

// 讄var的类型ؓ数组

    var.vt = VT_ARRAYVT_UI1;

// var和我们的单维SAFEARRAY拉上关系Q?/p>

    var.parray = lpsa;

}

 

if (pbData)

{

     //相应用来释放数据的函数是SafeArrayUnaccessData()Q该功能释放该参数的计数?/p>

    SafeArrayUnaccessData(var.parray);

}

if (FAILED(hr))

{

     // 销毁SAFEARRAY

    SafeArrayDestroy(lpsa);

}

 

return hr;

}

 

////////////////////////////////////////////////////////////

//Added Headers:

////////////////////////////////////////////////////////////

#include <comdef.h>

#include <atlbase.h>

///////////////////////////////////////////////////////////

//Added for MSMQ:

///////////////////////////////////////////////////////////

#import "mqoa.dll" no_namespace, named_guids

typedef    struct  _DATA 

       int    _n; 

       char   _str;

}_DATA;

//main:

{

.. ..

.. ..

IMSMQMessagePtr pisMsg = NULL;

hr = pisMsg.CreateInstance("MSMQ.MSMQMessage");

_DATA msg;

msg._n = 1;

msg._str = '1';

CComVariant var;

// 打包函数Q?/p>

ChangeStruct2Variant(var, &msg);

// 打包后的CComVariant传递给MSMQMessege的Body属性:

pisMsg->Body= var;

pisMsg->AppSpecific=-1;

// 发送到消息队列Q?/p>

pisMsg->Send(pisQueue);

.. ..

}
 

 

q样Q就可以成功地把一个结构递交到MSMQ队列中了?/p>

 

Implementation ScopeQ?br>l箋阅读之前Q我们假设?zhn)熟(zhn)以下知识Q?/p>

  • SAFEARRAY
  • ISTREAM
  • Microsoft MSMQ

目录Q?/p>

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream打包传递数据到MSMQ队列

 

下面l出dMSMQ消息时解析的步骤QVC++代码Q:

////////////////////////////////////////////////////////////

//Added Headers:

////////////////////////////////////////////////////////////

#include <comdef.h>

#include <atlbase.h>

///////////////////////////////////////////////////////////

//Added for MSMQ:

///////////////////////////////////////////////////////////

#import "mqoa.dll" no_namespace, named_guids

typedef    struct  _DATA 

       int    _n; 

       char   _str;

}_DATA;

//main:

{

.. ..

.. ..

hr = pisQI->raw_Open(MQ_PEEK_ACCESS,MQ_DENY_NONE,&pisQueue);

IMSMQMessagePtr piMessage;

// 获取MSMQ队列中的一个消息:

piMessage = pisQueue->PeekCurrent();

_DATA *msg = new _DATA();

// 解析函数Q?/p>

ChangeVariant2Struct(CComVariant(piMessage->Body), msg);

.. ..

}

 

// ChangeVariant2Struct函数的定义:

// W一个参敎ͼ

//   cdQCComVariant

//   作用Q源

// W二个参敎ͼ

//   cdQ_DATA*

//   作用Q接收?/p>

HRESULT ChangeVariant2Struct (CComVariant &var, _DATA *DP)

{

SAFEARRAY* psa;

BYTE HUGEP *lpb;

psa = var.parray;

SafeArrayAccessData(psa, (void HUGEP **)&lpb);

CopyMemory((LPVOID)DP, (LPVOID)lpb, 8);

SafeArrayUnaccessData(psa);

 

return S_OK;

}
 


Writen by zhengyun.NoJunk(at)tomosoft.dot.com

DisclaimersQ?br> 

本文档仅供参考。本文档所包含的信息代表了在发布之日,zhengyunҎ(gu)讨论问题的当前看法,zhengyun不保证所l信息在发布之日以后的准性?

用户应清楚本文档的准性及其用可能带来的全部风险。可以复制和传播本文档,但须遵守以下条款Q?

复制时不得修改原文,复制内容d含所有页 Q?
所有副本均d?zhengyun的版权声明以及所提供的其它声?Q?/p>


Implementation ScopeQ?br>l箋阅读之前Q我们假设?zhn)熟(zhn)以下知识Q?/p>

  • SAFEARRAY
  • ISTREAM
  • Microsoft MSMQ

目录Q?/p>

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream打包传递数据到MSMQ队列

 

3.借用IStream传递数?br>正如前面所qͼ当你有一块非常巨大的数据要传递给MSMQ队列Ӟ而且你希望一ơ液压成型,那么把它打包入IStream,也是一个很常用技巧,了解COM的h都知道,我也不多解释了?/p>

我们研究了ATL中IPersistMemoryImpl接口LoadҎ(gu)的实现机理,来做我们的事情:

// 函数名:LoadStreamOnHugeMemory

// 功能Q?nbsp; 同上

// W一个参数pvMem指向一块要打包的内存,W二个参数指明这块内存的大小

HRESULT LoadStreamOnHugeMemory(void pvMem, ULONG cbSize)

{

     // Get Memory Handle:

     HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, cbSize);

     If(NULL == h) return E_OUTOFMEMORY;

     LPVOID pv = GlobalLock(h);

     If(!pv) return E_OUTOFMEMORY;

    

     // Copy to memory block

     CopyMemory(pv, pvMem, cbSize);

     CComPtr<IStream> spStream;

     // Create stream on Memory:

     HRESULT hr = CreateStreamOHGlobal(h, TRUE, &spStream);

     If(FAILED(hr))

     {

          GlobalUnlock(h);

          GlobalFree(h);

          return hr;

     }

     // stream now owns the memory

 

// unlock the data

GlobalUnlock(hGlobal);

 

// Create a stream holder. Load the stream holder from the global stream.

//  THIS STREAM HOLDER IS INTERITED FROM IPersistStream

//  And all virtual functions are Modified to handle the object....

CComPtr <IStreamHolder> pHolder = new CComObject <CStreamHolder>;

 

CComPtr <IPersistStream> pHolderStream;

hr = pHolder->QueryInterface (IID_IPersistStream, (void **)&pHolderStream);

 

pStream->Seek( zero, STREAM_SEEK_SET, NULL );

pHolderStream->Load(pStream);

 

CComVariant vComData = pHolder;

.. ..

 

//

// now, you have a big chunk of memory loaded into a ComVariant

//

// 现在你可以把打包后的CComVariant传递给MSMQMessege的Body属性了Q?/p>

pisMsg->Body = vComData;

.. ..

}
 

 

其实QAydin的实现和ATL中IPersistMemoryImpl接口LoadҎ(gu)实现异曲同工。我们不妨换一U方式实现?/p>

 

Writen by zhengyun.NoJunk(at)tomosoft.dot.com

DisclaimersQ?br>本文档仅供参考。本文档所包含的信息代表了在发布之日,zhengyunҎ(gu)讨论问题的当前看法,zhengyun不保证所l信息在发布之日以后的准性?

用户应清楚本文档的准性及其用可能带来的全部风险。可以复制和传播本文档,但须遵守以下条款Q?

复制时不得修改原文,复制内容d含所有页 Q?
所有副本均d?zhengyun的版权声明以及所提供的其它声?Q?
不得以赢利ؓ目的Ҏ(gu)文档q行传播 ?/p>

 

Implementation ScopeQ?br>l箋阅读之前Q我们假设?zhn)熟(zhn)以下知识Q?/p>

  • SAFEARRAY
  • ISTREAM
  • Microsoft MSMQ

目录Q?/p>

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream打包传递数据到MSMQ队列

 

其实QAydin的实现和ATL中IPersistMemoryImpl接口LoadҎ(gu)实现异曲同工。我们不妨换一U方式实现?/p>

Aydinl出的VC++代码是:

// 指?/p>

CComPtr<IStream> pStream = NULL;

LARGE_INTEGER zero = {0,0};

// hGlobal是内存句柄:

LPBYTE pChunk = (BYTE *) GlobalLock(hGlobal);

 

// 创徏一个空的streamQ?/p>

HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream );

pStream->Seek( zero, STREAM_SEEK_SET, NULL );

 

ULONG pcbWritten = 0;

// pChunk现在已经指向我们的巨大内存块Q?/p>

// 我们把这块内存写入IStream中Q?/p>

pStream->Write (pChunk, dwNumRead, &pcbWritten);

// 查是否全部写入了Q?/p>

ATLASSERT(pcbWritten==dwNumRead);

 

// unlock the data

GlobalUnlock(hGlobal);

 

// 剩下的一P也是让CComPtr <IPersistStream>来调用LoadҎ(gu)加蝲IStream

 

// Create a stream holder. Load the stream holder from the global stream.

//  THIS STREAM HOLDER IS INTERITED FROM IPersistStream

//  And all virtual functions are Modified to handle the object....

CComPtr <IStreamHolder> pHolder = new CComObject <CStreamHolder>;

 

CComPtr <IPersistStream> pHolderStream;

hr = pHolder->QueryInterface (IID_IPersistStream, (void **)&pHolderStream);

 

pStream->Seek( zero, STREAM_SEEK_SET, NULL );

pHolderStream->Load(pStream);

 

CComVariant vComData = pHolder;

.. ..

 

//

// now, you have a big chunk of memory loaded into a ComVariant

//

// 现在你可以把打包后的CComVariant传递给MSMQMessege的Body属性了Q?/p>

pisMsg->Body = vComData;

.. ..

 

//Coder: Aydin T.BAKIR
 


 全文完?/p>

Writen by zhengyun.NoJunk(at)tomosoft.dot.com

DisclaimersQ?br> 

本文档仅供参考。本文档所包含的信息代表了在发布之日,zhengyunҎ(gu)讨论问题的当前看法,zhengyun不保证所l信息在发布之日以后的准性?

用户应清楚本文档的准性及其用可能带来的全部风险。可以复制和传播本文档,但须遵守以下条款Q?

复制时不得修改原文,复制内容d含所有页 Q?
所有副本均d?zhengyun的版权声明以及所提供的其它声?Q?
不得以赢利ؓ目的Ҏ(gu)文档q行传播 ?/p>

]]>
Windows区对?Bands)的创Z定制http://www.shnenglu.com/mydriverc/articles/29022.html旅?/dc:creator>旅?/author>Mon, 30 Jul 2007 08:20:00 GMThttp://www.shnenglu.com/mydriverc/articles/29022.htmlhttp://www.shnenglu.com/mydriverc/comments/29022.htmlhttp://www.shnenglu.com/mydriverc/articles/29022.html#Feedback0http://www.shnenglu.com/mydriverc/comments/commentRss/29022.htmlhttp://www.shnenglu.com/mydriverc/services/trackbacks/29022.html

 

Windows区对?Bands)的创Z定制
~译/赉|?/font>

本文例子代码

1 ?/font>
  1.1 览栏区对象
  1.2 工具栏区对象

  1.3 必须实现的接?/font>

    3.3.1 IUnknown
    3.3.2 IObjectWithSite
    3.3.3 IPersistStream
    3.3.4 IDeskBand
  3.4 一??/a>
Windows的区QBandsQ对象有三种Q既览栏(Explorer BarQ区对象Q工hQTools BandsQ区对象Q和桌面区对象(Desk BandsQ?br>
览栏区对象
览栏区对象U浏览栏Q它是从IE4.0引入的,它是邻近览器窗格的一个显C区域。实际上它是IEH口中的一个子H口Q可以用它来昄信息及与用户交互。浏览栏卛_以是以垂直方式定位在览器窗格的左边。也可以水^方式定位在浏览器H格下面。(如图一Q?

图一
在浏览栏中可以创建很多子菜单或选项Q用戯以不同方式选择q些子菜单或选项提供的功能,打开IE或者资源管理器Q从“查看”菜单中选择“览?#8221;Q可以看到Windows提供了几U标准的览栏菜单,?#8220;搜烦QSearchQ?#8221;,“收藏夹(FavoritesQ?#8221;Q??#8220;历史记录QHistoryQ?#8221;,以及“文g夹(All FoldersQ?#8221;。(如图二)

图二
Z创徏定制的浏览栏Q必ȝE实玎ͼ然后注册它们。Windows在外壻IShellQ?.71中引入了区对象。它提供与普通窗口一L功能。但因ؓ它是以IE或外壳ؓ容器的COM对象Q所以实现v来就与普通窗口有所不同。图一中显C的是一个简单的览栏例子。图中有一个垂直的览栏和一个水q的览栏?br>
工具栏区对象
工具栏区对象U工hQ它是在IE5.0中引入用以支持单选工hQradio toolbarQ特性的。IE工具栏实际上是一个Rebar控gQ它包含了几个工hQtoolbarQ控件。通过创徏工具栏,你可以将某个区对象功能添加到Rebar控g中。不论是在IE中还是在资源理器中Q区对象都是一LQ所以工h也是一个通用H口。(如图三)

图三

用户可以?#8220;查看”菜单中的“工具?#8221;子菜单中选择昄单选工hQ也可以在工h区域单击鼠标右键从它的上下文菜单中选择昄单选工h?br>

区对象也可以用在桌面Q也是创徏桌面区对象。虽然它们的基本实现与浏览栏cMQ但桌面ZIE没有关系Q它不用IE作ؓ容器。它主要用来创徏桌面动H口。通过在Q务栏上单d键,然后在弹出的菜单中选择“工具?#8221;的子菜单选项。(如图四)

囑֛

桌面区的初始动位置在Q务栏Q(如图?br>
图五

用户可以桌面区拖到桌面上,q时它就成了一个普通窗口:Q如囑օQ?br>
囑օ
二、实现区对象
管可以像用普通窗口一样用区对象Q但它们毕竟是COM对象Q存在于某个容器之中。如览栏和工具栏位于IE之中Q桌面区位于外壳之中。虽然它们的功能不同Q但其基本实现非常相伹{一个主要的差别是它们的注册方式不同Q而注册方式的不同又决定了对象的类型及其容器。这一部分我们先讨论所有区对象实现的共性。其它的实现l节可参?a target=_self>垂直览栏例子程?/font>?br>区对象除了要实现 IUnknown ?IClassFactory 两个接口之外Q所有的区对象还必须实现以下q几个接口:
  •  IDeskBand 
  •  IObjectWithSite 
  •  IPersistStream
另外Q在注册旉了注册它们的CLSID之外Q浏览栏和桌面区对象q必进行组件类别(categoryQ的注册。它军_了对象的cd及其容器。工h不需要进行种cL册。归Uv来,需要进行CATID注册的三U区对象是:
区对象类?/strong> lgcd
垂直览?/td> CATID_InfoBand
水^览?/td> CATID_CommBand
桌面?/td> CATID_DeskBand

对于如何注册区对象的q一步讨参见注册部分?br>如果某个区对象接受用戯入,它还必须实现IInputObject接口。如果要往上下文菜单中d菜单目Q还必须实现IContextMenu接口。注意:工具栏区对象不支持上下文菜单?br>    因ؓ区对象实现的是子H口Q所以它们还必须有窗口过E来处理Windows的消息?br>    区对象可以通过其IOleCommandTarget接口发送命令到它的容器。ؓ了得到这个接口的指针Q必调用容器的IInputObjectSite::QueryInterfaceҎ(gu)?/xxxxime>hIID_IoleCommandTarget。然后用IOleCommandTarget::Exec把命令发送到容器。命令组是CGID_DeskBand。当某个区对象的IDeskBand::GetBandInfoҎ(gu)被调用时Q容器用dwBandID参数一个标C符赋给q个对象。这个标C符被用于IOleCommandTarget::ExecҎ(gu)调用时所用命令组中的三个命o。目前命令组共支持四个IOleCommandTarget::Exec命oIDs。这四个命o的解释如下:
DBID_BANDINFOCHANGED——Band的信息已改变。参数pvaIn的值应该是最q一ơ调用所用的band标示W。容器将调用q个标示W所指的band对象的IDeskBand::GetBandInfoҎ(gu)h更新的信息?br>DBID_MAXIMIZEBAND——容器将最大化band。参数pvaIn的值应该是最q一ơ调用所用的band标示W?br>DBID_SHOWONLY——关闭或打开容器中其它band。参数pvaIn的gؓVT_UNKNOWNcdQ可以取下列g一Q?br>
?/td> 描述
pUnk q个对象IUnknown接口的指针。所有其它的桌面band被隐藏?/td>
0 隐藏所有桌面band?/td>
1 昄所有桌面band?/td>
DBID_PUSHCHEVRON——目前没有实现?br>
注册
区对象必ME内服务器(in-processQ注册。其U程模型必须?#8220;Apartment”。也是说区对象必须以DLL的Ş式来实现。用来描q服务器注册条目的缺省值是一个菜单文本串。就拿浏览栏来说。这个菜单出现在资源理器或IE “查看QViewQ?#8221;菜单?#8220;览栏(Explorer BarQ?#8221;子菜单中。而工h的菜单则出现在资源管理器或IE “查看QViewQ?#8221;菜单?#8220;工具栏(ToolbarsQ?#8221;子菜单中。桌面区出现在Q务栏上下文菜单的“工具栏(ToolbarsQ?#8221;子菜单中。作单资源,提供键盘快捷的方法与一般菜单快捷键相同。也是?#8220;&”字符攑֜某个单词字母前表C个字母显CZ划线来指C快捷键?br>通常区对象的注册条目如下:
HKEY_CLASSES_ROOT
                        ...
                        CLSID
                        ...
                        {Band 对象?CLSID GUID} = "菜单文本?
                        InProcServer32 = "DLL 路径?
                        ThreadingModel = "Apartment"
工具栏区对象必须q要注册对象的CLSID。ؓ此必dHKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Toolbar下创Z个REG_SZ|用工h区对象的CLSID GUID串命名。如Q?br>
HKEY_LOCAL_MACHINE
                        Software
                        Microsoft
                        Internet Explorer
                        Toolbar
                        { Band 对象?CLSID GUID }

除此之外Q还有几个可选的注册值可以加到注册表中,本文的例子中未用这些倹{?
  • HKEY_CLASSES_ROOT\CLSID\{Band 对象?CLSID GUID}\Instance\CLSID, 它应该被讄?"{4D5C8C2A-D075-11D0-B416-00C04FB90376}". 
  • HKEY_CLASSES_ROOT\CLSID\{Band对象的CLSID GUID}\Instance\InitPropertyBag\Url 它应该被讄在浏览栏昄的包含HTML内容的文件位|?br>
  • \HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Explorer Bars\{Band 对象?CLSID GUID}\BarSize 它应该被讄为栏目的高和宽,它需要八个字节才能作Z攑օ注册表,字节之间用逗号分开。开始的四个字节一像素为单位指定大,格式要用十六q制Q从最左边字节开始。最后四个字节是保留字节Q应该将它置为零。例如,垂直览栏的~省宽度?91Q?x123Q像素,则BarSize 的值应该是"23,01,00,00,00,00,00,00" 
如果要用览栏显CHTMLQ则前两个注册项是必ȝ。最后一个注册项则根据垂直的或者水q的览栏定义相应的~省宽度和高度?br>能显CHTML的浏览栏Q缺省宽度ؓ291各像素单位)注册表条目的形式如下Q?br>
HKEY_CLASSES_ROOT
                        ...
                        CLSID
                        ...
                        {Band 对象?CLSID GUID} = "菜单文本?
                        InProcServer32 = "DLL 路径?
                        ThreadingModel = "Apartment"
                        Instance
                        CLSID = "{4D5C8C2A-D075-11D0-B416-00C04FB90376}"
                        InitPropertyBag
                        Url = "HTML文g"
                        ...
                        HKEY_CURRENT_USER
                        ...
                        Software
                        ...
                        Microsoft
                        ...
                        Internet Explorer
                        ...
                        Explorer Bars
                        { Band 对象?CLSID GUID }
                        BarSize = "23,01,00,00,00,00,00,00"

你可以通过~程的方式来处理区对象类?CATID 的注册。创Z个组件类别管理器对象(CLSID_StdComponentCategoriesMgr)q请求一个指向ICatRegister接口的指针。将区对象的CLSID和CATID传递到ICatRegister::RegisterClassImplCategories?br>三、定制浏览栏的一个简单例?/a>
q个例子展示了前面所介绍q的垂直览栏的整个实现q程。它借助了^台SDKQPlatform SDK——在msdn中可以找刎ͼ中关于band对象C代码。其中还包括了水qx览栏和桌面band的实C码。详l实现细节请参见QCommBand.cpp和DeskBand.cpp?br>创徏定制览栏的基本q程是这LQ?br>
  1. 实现DLL需要的函数?
  2. 实现必须的COM接口?
  3. 实现M惌的可选接口?
  4. 注册对象的CLSID?
  5. q行恰当的组件种cL册?
  6. 创徏IE子窗口,调整H口大小适合览栏的昄区域?
  7. 使用子窗口显CZ息ƈ与用户交互?
实际上,只要通过恰当的组件种cL册,览栏例子代码便既可用于览栏的实现Q也能用于桌面band实现。更加复杂的实现需要定制每U对象类型的昄区域和容器。但大多数的定制工作都能通过范例代码以及Windows子窗口的~程技术来完成。例如,你可以添加用户交互控制或者进行色彩丰富的囑Ş昄处理?br>
DLL函数
所有三U区对象被打包在一个DLL中,它输Z下的函数Q?br>
  • DllMain
  • DllCanUnloadNow 
  • DllGetClassObject 
  • DllRegisterServer 
q些函数可以在BandObjs.cpp中找刎ͼ它们服务于所有三U区对象。前三个函数乃标准的实现Q我们不再本文中讨论。类工厂也是标准实现Q代码可以在ClsFact.cpp中找?br>
注册定制的浏览栏

有了COM对象后,必须Ҏ(gu)览栏的CLSIDq行注册。另外如果要与IE或资源管理器
协调q行Q还必须q行的恰当的lgU类QCATID_InfoBandQ注册。这个工作由DllRegisterServer处理。浏览栏例子代码有关的处理部分如下:
...
                        //注册览栏对?
                        if(!RegisterServer(CLSID_SampleExplorerBar, TEXT("垂直览栏例?)))
                        return SELFREG_E_CLASS;
                        //注册览栏的对象lgU类
                        if(!RegisterComCat(CLSID_SampleExplorerBar, CATID_InfoBand))
                        return SELFREG_E_CLASS;
                        ...
区对象的注册使用通常的COMq程Q它q有函数RegisterServer处理?br>除了CLSID之外Q这个区对象服务器还必须注册一个以上的lgU类。这实际上是垂直览栏和水^览栏实C间的主要差别。这个过E的处理是通过创徏一个组件种cȝ理器对象QCLSID_StdComponentCategoriesMgrQ,q用ICatRegister::RegisterClassImplCategoriesҎ(gu)来注册区对象服务器。在q个例子中,lgU类注册的处理是通过浏览栏的CLSID和CATID传递到U有函数RegisterComCat完成的:
BOOL RegisterComCat(CLSID clsid, CATID CatID)
                        {
                        ICatRegister   *pcr;
                        HRESULT        hr = S_OK ;
                        CoInitialize(NULL);
                        hr = CoCreateInstance(  CLSID_StdComponentCategoriesMgr,
                        NULL,
                        CLSCTX_INPROC_SERVER,
                        IID_ICatRegister,
                        (LPVOID*)&pcr);
                        if(SUCCEEDED(hr))
                        {
                        hr = pcr->RegisterClassImplCategories(clsid, 1, &CatID);
                        pcr->Release();
                        }
                        CoUninitialize();
                        return SUCCEEDED(hr);
                        }


垂直览栏例子实C四个必须的接口:IUnknown, IObjectWithSite, IPersistStream, 和IDeskBandQ它们都在CExplorerBarcM实现?br>IUnknown
构造函敎ͼ析构函数和IUnknown实现比较单,本文在此不讨论。细节请参见源代码?br>IObjectWithSite接口
当用户选择某个览栏时Q容器调用相应band对象的IObjectWithSite::SetSiteҎ(gu)。参数将被设|成q个现场QSiteQ的IUnknown指针?br>通常QSetSite实现应该完成下列步骤Q?br>
  1. 释放当前所把持的Q何现场指针?
  2. 如果传递到SetSite的指针被|ؓNULLQ此则区对象被删除。SetSite可以q回S_OK?
  3. 如果传递到SetSite的指针被|ؓ非NULLQ则建立新的现场。SetSite应该做以下的事情Q?br>
  1. 调用现场QueryInterfaceҎ(gu)hIOleWindow接口?
  2. 调用IOleWindow::GetWindow获取父窗口句柄,q存储它Q以便以后用。如果不再用的话,释放IOleWindow接口?
  3. 创徏此band对象的窗口ؓ一个子H口Q其父窗口就是上一步获得的那个H口。注意在此不能将它创建成可见H口?
  4. 如果此band对象实现IInputObjectQ调用现场QueryInterfaceҎ(gu)hIInputObjectSite接口Q存储这个接口的指针以备后用?
  5. 如果所有步骤都成功Q则q回S_OKQ否则返回OLE定义的错误代码以指示错误cd?
以下是浏览栏实现SetSite的方法。m_pSite是私有成员变量,用它来保存I(y)InputObjectSite指针Q而m_hwndParent保存父窗口句柄?br>
STDMETHODIMP CExplorerBar::SetSite(IUnknown* punkSite)
                        {
                        //如果某个现场被把持,则释攑֮
                        if(m_pSite)
                        {
                        m_pSite->Release();
                        m_pSite = NULL;
                        }
                        //如果punkSite 不ؓNULL, 建立一个新的现?
                        if(punkSite)
                        {
                        //获取父窗?
                        IOleWindow  *pOleWindow;
                        m_hwndParent = NULL;
                        if(SUCCEEDED(punkSite->QueryInterface(IID_IOleWindow, (LPVOID*)&pOleWindow)))
                        {
                        pOleWindow->GetWindow(&m_hwndParent);
                        pOleWindow->Release();
                        }
                        if(!m_hwndParent)
                        return E_FAIL;
                        if(!RegisterAndCreateWindow())
                        return E_FAIL;
                        //获取柄保存I(y)InputObjectSite指针
                        if(SUCCEEDED(punkSite->QueryInterface(IID_IInputObjectSite, (LPVOID*)&m_pSite)))
                        {
                        return S_OK;
                        }
                        return E_FAIL;
                        }
                        return S_OK;
                        }
q个例子的GetSite只简单地用SetSite保存的现场指针实C对现场QueryInterfaceҎ(gu)的调用?br>
STDMETHODIMP CExplorerBar::GetSite(REFIID riid, LPVOID *ppvReturn)
                        {
                        *ppvReturn = NULL;
                        if(m_pSite)
                        return m_pSite->QueryInterface(riid, ppvReturn);
                        return E_FAIL;
                        }
H口创徏q有方法RegisterAndCreateWindow负责。如果这个窗口不存在Q此Ҏ(gu)浏览栏H口创徏成一个大适当的子H口Q它的父H口是由SetSite获得的那个窗口。子H口的句柄存储在m_hwnd变量中?br>
BOOL CExplorerBar::RegisterAndCreateWindow(void)
                        {
                        //如果q个H口不存在,则创建它
                        if(!m_hWnd)
                        {
                        //子窗口不能没有父H口
                        if(!m_hwndParent)
                        {
                        return FALSE;
                        }
                        //如果H口cL有注册,则必L?
                        WNDCLASS wc;
                        if(!GetClassInfo(g_hInst, EB_CLASS_NAME, &wc))
                        {
                        ZeroMemory(&wc, sizeof(wc));
                        wc.style          = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
                        wc.lpfnWndProc    = (WNDPROC)WndProc;
                        wc.cbClsExtra     = 0;
                        wc.cbWndExtra     = 0;
                        wc.hInstance      = g_hInst;
                        wc.hIcon          = NULL;
                        wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
                        wc.hbrBackground  = (HBRUSH)CreateSolidBrush(RGB(0, 0, 192));
                        wc.lpszMenuName   = NULL;
                        wc.lpszClassName  = EB_CLASS_NAME;
                        if(!RegisterClass(&wc))
                        {
                        //如果注册p|Q下面的CreateWindow函数失?
                        }
                        }
                        RECT  rc;
                        GetClientRect(m_hwndParent, &rc);
                        //创徏q个H口。WndProc 徏立m_hWnd变量
                        CreateWindowEx(   0,
                        EB_CLASS_NAME,
                        NULL,
                        WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER,
                        rc.left,
                        rc.top,
                        rc.right - rc.left,
                        rc.bottom - rc.top,
                        m_hwndParent,
                        NULL,
                        g_hInst,
                        (LPVOID)this);
                        }
                        return (NULL != m_hWnd);
                        }
IPersistStream接口
IE调用浏览栏的IPersistStream接口Q以便允许这个浏览栏加蝲或存储持久性数据。如果没有持久性数据,q个Ҏ(gu)仍然必须q回一个成功代码。IPersistStream接口从IPersistl承而来Q所以要实现五个Ҏ(gu)Q?br>GetClassID, IsDirty, Load, Save, GetSizeMax?br>本文的这个浏览栏例子不用持久性数据,q且只有IPersistStream的最实现。GetClassIDq回对象的CLSIDQCLSID_SampleExplorerBarQ,其余的方法返回S_OK, 或者S_FALSE, 或?E_NOTIMPL。有关细节请参见IPersistStream的实现?br>
IDeskBand接口
IDeskBand接口是区对象专用接口。它只有一个方法。IDeskBand接口从IDockingWindowl承而来Q而IDockingWindow又从IOleWindowl承而来?br>IOleWindow有两个方法:GetWindow ?ContextSensitiveHelp。浏览栏例子的GetWindow实现q回览栏的子窗口句柄m_hwnd。因Z实现上下文敏感帮助,所以ContextSensitiveHelpq回E_NOTIMPL?br>IDockingWindow接口有三个方法:ShowDW, CloseDW, ?ResizeBorder。ResizeBorder不在M区对象中使用Q应该返回E_NOTIMPL。ShowDWҎ(gu)Ҏ(gu)其不同的参数值控制浏览栏H口的显C或隐藏Q?br>
STDMETHODIMP CExplorerBar::ShowDW(BOOL fShow)
                        {
                        if(m_hWnd)
                        {
                        if(fShow)
                        {
                        //昄H口
                        ShowWindow(m_hWnd, SW_SHOW);
                        }
                        else
                        {
                        //隐藏H口
                        ShowWindow(m_hWnd, SW_HIDE);
                        }
                        }
                        return S_OK;
                        }
                        CloseDWҎ(gu)摧毁览栏窗口:
                        STDMETHODIMP CExplorerBar::CloseDW(DWORD dwReserved)
                        {
                        ShowDW(FALSE);
                        if(IsWindow(m_hWnd))
                        DestroyWindow(m_hWnd);
                        m_hWnd = NULL;
                        return S_OK;
                        }
其余的方法,如GetBandInfo是IDeskBand专用的。IE使用它来指定览栏的标示W以及视图模式。IEq可能填写DESKBANDINFOl构的dwMask成员从浏览栏h更多的信息,q个l构用第三个参数传递。GetBandInfo应该存储q个标示W和视图模式q用所h的数据填写DESKBANDINFOl构。下面是本文览栏例子所实现GetBandInfoQ?br>
STDMETHODIMP CExplorerBar::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi)
                        {
                        if(pdbi)
                        {
                        m_dwBandID = dwBandID;
                        m_dwViewMode = dwViewMode;
                        if(pdbi->dwMask & DBIM_MINSIZE)
                        {
                        pdbi->ptMinSize.x = MIN_SIZE_X;
                        pdbi->ptMinSize.y = MIN_SIZE_Y;
                        }
                        if(pdbi->dwMask & DBIM_MAXSIZE)
                        {
                        pdbi->ptMaxSize.x = -1;
                        pdbi->ptMaxSize.y = -1;
                        }
                        if(pdbi->dwMask & DBIM_INTEGRAL)
                        {
                        pdbi->ptIntegral.x = 1;
                        pdbi->ptIntegral.y = 1;
                        }
                        if(pdbi->dwMask & DBIM_ACTUAL)
                        {
                        pdbi->ptActual.x = 0;
                        pdbi->ptActual.y = 0;
                        }
                        if(pdbi->dwMask & DBIM_TITLE)
                        {
                        lstrcpyW(pdbi->wszTitle, L"览栏例?);
                        }
                        if(pdbi->dwMask & DBIM_MODEFLAGS)
                        {
                        pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;
                        }
                        if(pdbi->dwMask & DBIM_BKCOLOR)
                        {
                        //通过Udq个标志来用默认的背景颜色
                        pdbi->dwMask &= ~DBIM_BKCOLOR;
                        }
                        return S_OK;
                        }
                        return E_INVALIDARG;
                        }

׃个接口的实现是可选择的,一个是IInputObjectQ另一个是 IContextMenu。本文的览栏例子实CIInputObject。对于IContextMenu的实现细节请参考有x档?br>
IInputObject接口
如果某个band对象要接受用戯入。那必d现IInputObject接口。IE实现IInputObjectSiteq用IInputObjectl护用户的输入焦炏V浏览栏需要实C个方法:UIActivateIO, HasFocusIO, ?TranslateAcceleratorIO?br>IE调用UIActivateIO通知览栏它以被ȀzL者被|灰。当被激zLQ浏览栏例子调用SetFocus来设|窗口输入焦炏V?br>当要定哪个H口有输入焦Ҏ(gu)QIE调用HasFocusIO。如果浏览栏的窗口或它的子窗口之一有输入焦点,HasFocusIOq回S_OK。否则,它返回S_FALSE?br>TranslateAcceleratorIO允许对象处理键盘加速键。本文浏览栏例子没有实现q个Ҏ(gu)Q所以它q回S_FALSE?br>览栏例子实现IInputObjectSite的细节如下:
STDMETHODIMP CExplorerBar::UIActivateIO(BOOL fActivate, LPMSG pMsg)
                        {
                        if(fActivate)
                        SetFocus(m_hWnd);
                        return S_OK;
                        }
                        STDMETHODIMP CExplorerBar::HasFocusIO(void)
                        {
                        if(m_bFocus)
                        return S_OK;
                        return S_FALSE;
                        }
                        STDMETHODIMP CExplorerBar::TranslateAcceleratorIO(LPMSG pMsg)
                        {
                        return S_FALSE;
                        }
H口q程
因ؓ区对象的昄用的是子H口Q所以它必须实现H口q程来处理Windows消息。浏览栏例子实现了一个最单的版本Q它的窗口过E只处理了五个消息:WM_NCCREATE, WM_PAINT, WM_COMMAND, WM_SETFOCUS, ?WM_KILLFOCUS。如果要实现更多的功能,很容易扩充它处理其它的消息?br>
LRESULT CALLBACK CExplorerBar::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
                        {
                        CExplorerBar  *pThis = (CExplorerBar*)GetWindowLong(hWnd, GWL_USERDATA);
                        switch (uMessage)
                        {
                        case WM_NCCREATE:
                        {
                        LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
                        pThis = (CExplorerBar*)(lpcs->lpCreateParams);
                        SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);
                        //讄H口句柄
                        pThis->m_hWnd = hWnd;
                        }
                        break;
                        case WM_PAINT:
                        return pThis->OnPaint();
                        case WM_COMMAND:
                        return pThis->OnCommand(wParam, lParam);
                        case WM_SETFOCUS:
                        return pThis->OnSetFocus();
                        case WM_KILLFOCUS:
                        return pThis->OnKillFocus();
                        }
                        return DefWindowProc(hWnd, uMessage, wParam, lParam);
                        }
q里WM_COMMAND消息处理器简单地q回零。WM_PAINT消息处理器创建文本ƈ昄在资源管理器或IE的区对象中?br>
LRESULT CExplorerBar::OnPaint(void)
                        {
                        PAINTSTRUCT ps;
                        RECT        rc;
                        BeginPaint(m_hWnd, &ps);
                        GetClientRect(m_hWnd, &rc);
                        SetTextColor(ps.hdc, RGB(255, 255, 255));
                        SetBkMode(ps.hdc, TRANSPARENT);
                        DrawText(ps.hdc, TEXT("览栏例?), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                        EndPaint(m_hWnd, &ps);
                        return 0;
                        }
WM_SETFOCUS ?WM_KILLFOCUS消息处理器通过调用本现场的IInputObjectSite::OnFocusChangeISҎ(gu)通知输入焦点现场改变Q?br>
LRESULT CExplorerBar::OnSetFocus(void)
                        {
                        FocusChange(TRUE);
                        return 0;
                        }
                        LRESULT CExplorerBar::OnKillFocus(void)
                        {
                        FocusChange(FALSE);
                        return 0;
                        }
                        void CExplorerBar::FocusChange(BOOL bFocus)
                        {
                        m_bFocus = bFocus;
                        //通知焦点已改变的输入对象现场
                        if(m_pSite)
                        {
                        m_pSite->OnFocusChangeIS((IDockingWindow*)this, bFocus);
                        }
                        }
四、ȝ

区对象提供了灉|和强大的扩展方式Q通过定制览栏得IE的功能大为增强。桌面区的实现扩展了普通窗口的能力。尽需要一些对COM的编E,但终I以子窗口的形式提供了一U用L面。从而今后的许多这U编E实现都能用cM的Windows~程技术。虽然本文所讨论的例子只提供了有限的功能Q但它示范了区对象全部的Ҏ(gu),q且可以在此基础上进行扩充来创徏独特和功能强大的的用L面?






]]>
ATL 实现定制?IE 览器栏、工h和桌面工h http://www.shnenglu.com/mydriverc/articles/29017.html旅?/dc:creator>旅?/author>Mon, 30 Jul 2007 08:00:00 GMThttp://www.shnenglu.com/mydriverc/articles/29017.htmlhttp://www.shnenglu.com/mydriverc/comments/29017.htmlhttp://www.shnenglu.com/mydriverc/articles/29017.html#Feedback0http://www.shnenglu.com/mydriverc/comments/commentRss/29017.htmlhttp://www.shnenglu.com/mydriverc/services/trackbacks/29017.htmlATL 实现定制?IE 览器栏、工h和桌面工h

作者:杨老师

下蝲源代?/font>

关键?/strong>QBandQDesk BandQExplorer BandQTool BandQ浏览器栏,工具栏,桌面工具?br>
一、引a
  最q,׃工作的要求,我需要在 IE 上做一些开发工作。于是在 MSDN 上翻阅了一些资料,Ҏ(gu) MSDN 上的说明我用 ATL 胜利完成?#8220;资本家老板”分配的Q务?br>Qƈ且在白天睡觉的过E中梦到了老板l我加工资啦......Q?br>现在Q我?MSDN 上的原文资料Q经q翻译整理ƈ把一?ATL 的实现奉贤给 VCKBASE 上的朋友们?/p>


  在翻译的q程中,有两个词汇非怸好理解。第一个词?Band 对象Q词怸译?#8220;镶边、裙子边、带子、乐?.....”我的英文水^有限Q实在不知道应该译Z么词汇更合适。于是我毅然决然地决定:在如下的中,依然使用 band q个词!Q什么?没听明白Q我的意思就是说Q我不翻译这个词了)但到?Band 对象应该如何理解那?L图一Q?br>

图一

  图一中画U圈的地方,分别UC“垂直的浏览器?#8221;?#8220;水^的浏览器?#8221;?#8220;工具?#8221;?#8220;桌面工具?#8221;。这?#8220;?#8221;Q都可以?IE ?#8220;查看”菜单中或鼠标右键的上下文快捷方式菜单中显C或隐藏h。这些界面窗口的实现Q其实就是实CU?COM 接口对象Q而这个对象叫 band。这个概念实在是只能意会而无法言传的Q我M能在文章中把它翻译ؓ“L靠在 IE ȝ口边上的对象”吧?^_^
  另外Q还有一个词?site。这个很好翻译,?#8220;站点”Q。呵呵,我敢打包,如果你要能理解这个翻译在计算机类文章中的含义Q那只能恭喜你了,你的智慧太高了。(都是学计机软g的hQ做人的差距咋就q么大呢Q)在本文章中Qsite 可以q样理解QIE 的主框架四周Q就好比?#8220;汽R?#8221;Q那?band 对象Q就好比?#8220;汽R”。band 汽RL可以停靠?#8220;汽R?#8221;上。所以,site 是“站点”Q它也是 COM 接口的对象(IObjectWithSite、IInputObjectSiteQ?br>


3.1 基本 band 对象
  Band 对象Q从 Shell 4.71(IE 5.0) 开始提供支持。Band 是一?COM 对象Q必L在一个容器中M用,当然使用它们好象用普通窗口是一L。IE 是一个容器,桌面 Shell 也是一个容器,它们提供不同的函数功能,但基本的实现是相似的?br>  Band 对象分三U类型,览器栏 bandQExplorer bandsQ、工h bandQTool BandsQ和桌面工具?Desk bands)Q而浏览器?band 又有两种表现形式Q垂直和水^的。那?IE ?Shell 如何区分q加载这?bands 对象呢?Ҏ(gu)是:你要对不同的 band 对象Q在注册表中注册不同的组件类型(CATIDQ?br>

Band 样式

lgcd

CATID

垂直的浏览器?/td> CATID_InfoBand 00021493-0000-0000-C000-000000000046
水^的浏览器?/td> CATID_CommBand 00021494-0000-0000-C000-000000000046
桌面的工h CATID_DeskBand 00021492-0000-0000-C000-000000000046

  IE 工具栏不使用lgcd注册Q而是使用在注册进?CLSID 的登记方式。详l情况见 3.3?br>  在例子程序中Q实C全部四个cd?band 对象Q垂直浏览器?CVerticalBar)昄了一?HTML 文gQƈ且实C?IE ȝ口浏览网늚D{功能;水^的浏览器?CHorizontalBar)是一个编辑窗Q它同步昄当前|页?BODY 源文件内容;IE 工具?CToolBar)最单,只是d了一个空的工hQ桌面工h(CDeskBar)实现了一个单行编辑窗口,你可以在上面输入命o行或文g名称Q回车后它会执行 Shell 的打开动作?br>
3.2 必须实现?COM 接口
  Band 对象?IE ?Shell 的进E内服务器,所以它被包装在 DLL 中。而作?COM 对象Q它必须要实?IUnknown ?IClassFactory 接口。(大家可以不同操心Q因为我们用 ATL 写程序,q两个接口是不用我们自己写代码的。)另外QBand 对象q必d?IDeskBand、IObjectWithSite ?IPersistStream 三个接口Q?br>  IPersistStream 是持l性接口的一U。当 IE 加蝲 band 对象的时候,它通过q个接口?Load Ҏ(gu)传递属性值给对象Q让其进行初始化Q而当卸蝲前,IE 则调用这个接口的 Save Ҏ(gu)保存对象的属性。用 ATL 实现q个接口很简单:

class ATL_NO_VTABLE Cxxx : 	......	public IPersistStreamInitImpl, // dl承	......{public:	BOOL m_bRequiresSave; // IPersistStreamInitImpl 所必须的变?.....BEGIN_COM_MAP(CVerticalBar)	......	COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)	COM_INTERFACE_ENTRY2(IPersistStream, IPersistStreamInit)	COM_INTERFACE_ENTRY(IPersistStreamInit)	......END_COM_MAP()BEGIN_PROP_MAP(Cxxx)...... // d需要持l性的属性END_PROP_MAP()		
  上面的代码,其实实现的是 IPersistStreamInit 接口Q不q没有关p,因ؓ IPersistStreamInit z?IPersistStreamQ实例化了派生类Q自然就实例化了基类。在例子E序中,我只在桌面工h对象中添加了持箋性属性,用来保存和初始化“命o?#8221;。另?COM_INTERFACE_ENTRY2(AQB)表示的含义是Q如果想查询A接口的指针,则提供B接口指针来代ѝؓ什么可以这样那Q因为B接口z自A接口Q那么B接口的前几个函数必然是A接口的函CQ自然B接口的地址其实和A接口的地址是一L了?br>  IObjectWithSite ?IE 用来Ҏ(gu)件进行管理和通讯用的一个接口。必要实现q个接口?个函敎ͼSetSite() ?GetSite()。当 IE 加蝲 band 对象和释?band 对象的时候,都要调用 SetSite()函数Q那么在q个函数里正好是写初始化和释放操作代码的地方Q?
STDMETHODIMP Cxxx::SetSite(IUnknown *pUnkSite){	if( NULL == pUnkSite )	// 释放 band 的时?{		// 如果加蝲的时候,保存了一些接?	// 那么现在Q释攑֮	}	else	// 加蝲 band 的时?{		m_hwndParent = NULL;	// 装蝲 band 的父H口(是带有标题的那个框架窗?		// q个H口的句柄,是调?IUnknown::QueryInterface() 得到 IOleWindow		// 然后调用 IOleWindow::GetWindow() 而获得的?	CComQIPtr< IOleWindow, &IID_IOleWindow > spOleWindow(pUnkSite);		if( spOleWindow )	spOleWindow->GetWindow(&m_hwndParent);		if( !m_hwndParent )	return E_FAIL;				// 现在Q正好是建立子窗口的时机?	// 注意Q子H口建立的时候,不要使用 WS_VISIBLE 属?	... ...		// 在例子程序中Q用 CAxWindow 实现了一个能包容ActiveX的容器窗?垂直览器栏)		// 在例子程序中Q用 WIN API 函数 CreateWindow 实现了标准窗?水^览器栏、工h)		// 在例子程序中Q用 CWindowImpl 实现了一个包容窗?桌面工具?		/*********************************************************/		   以下部分Q根?band 对象Ҏ(gu)的功能,是可以选择实现?	**********************************************************/				// 如果子窗口实C用户输入Q那么必d?IInputObject 接口Q?	// 而该接口是被 IE ?IInputObjectSite 调用的,因此在你的对?	// 中,应该保存 IInputObjectSite 的接口指针?	// 在类的头文g中,定义Q?	// CComQIPtr< IInputObjectSite, &IID_IInputObjectSite > m_spSite;		m_spSite = pUnkSite;	// 保存 IInputObjectSite 指针		if( !m_spSite )		return E_FAIL;		// 你需要控?IE 的主框架吗?		// 那么在类的头文g中,定义Q?	// CComQIPtr< IWebBrowser2, &IID_IWebBrowser2 > m_spFrameWB;		// 然后Q先取得 IServiceProvider,再取?IWebBrowser2		CComQIPtr < IServiceProvider, &IID_IServiceProvider> spSP(pUnkSite);		if( !spSP )	return E_FAIL;		spSP->QueryService( SID_SWebBrowserApp, &m_spFrameWB );		if( !m_spFrameWB)	return E_FAIL;		// 如果你取得了 IE L架的 IWebBrowser2 指针		// 那么Q当它发生了什么事情,你难道不想知道吗Q?	// 定义QCComPtr m_spCP;		CComQIPtr< IConnectionPointContainer,			&IID_IConnectionPointContainer> spCPC( m_spFrameWB );		if( spCPC )		{			spCPC->FindConnectionPoint( DIID_DWebBrowserEvents2, &m_spCP );			if( m_spCP )			{				m_spCP->Advise( reinterpret_cast< IDispatch * >( this ), &m_dwCookie );			}		}		// 咳~~~ 不说了,看源码去吧。这里能q的事情太多?.. ...	}	return S_OK;}		
IDeskBand 是一个特D的 band 对象接口Q有一个方法函敎ͼGetBarInfo()Q?br>IDockingWindow ?IDeskBank 的基c,?个方法函敎ͼShowDW()、CloseDW()、ResizeBorderDW()Q?br>IOleWindow 又是 IDockingWindow 的基c,?个方法函敎ͼGetWindow()、ContextSensitiveHelp()Q?

  首先声明 IDeskBand ,然后要实?IDeskBand 接口的共6个函敎ͼq些函数比较单,不同cd?band 对象Q其实现Ҏ(gu)也都基本一_
class ATL_NO_VTABLE Cxxx : 	......	public IDeskBand,	......{......BEGIN_COM_MAP(Cxxx)	......	COM_INTERFACE_ENTRY_IID(IID_IDeskBand, IDeskBand)	......END_COM_MAP()// IOleWindowSTDMETHODIMP Cxxx::GetWindow(HWND * phwnd){	// 取得 band 对象的窗口句?// m_hWnd 是徏立窗口时候保存的	*phwnd = m_hWnd;		return S_OK;}STDMETHODIMP Cxxx::ContextSensitiveHelp(BOOL fEnterMode){	// 上下文帮助,参?IContextMenu 接口	return E_NOTIMPL;}// IDockingWindowSTDMETHODIMP CVerticalBar::ShowDW(BOOL bShow){	// 昄或隐?band H口	if( m_hWnd )		::ShowWindow( m_hWnd, bShow ? SW_SHOW : SW_HIDE);	return S_OK;}STDMETHODIMP CVerticalBar::CloseDW(DWORD dwReserved){	// 销?band H口	if( ::IsWindow( m_hWnd ) )		::DestroyWindow( m_hWnd );	m_hWnd = NULL;    return S_OK;}STDMETHODIMP CVerticalBar::ResizeBorderDW(LPCRECT prcBorder, IUnknown* punkToolbarSite, BOOL fReserved){	// 当框架窗口的Ҏ(gu)大小改变?return E_NOTIMPL;}// IDeskBandSTDMETHODIMP CVerticalBar::GetBandInfo(DWORD dwBandID, DWORD dwViewMode,  DESKBANDINFO* pdbi){	         // 取得 band 的基本信息,你需要填?pdbi 参数作ؓq回	if( NULL == pdbi )		return E_INVALIDARG;	// 如果来需要调?IOleCommandTarget::Exec() 则需要保存这2个参?m_dwBandID = dwBandID;	m_dwViewMode = dwViewMode;	if(pdbi->dwMask & DBIM_MINSIZE)	{	// 最尺?	pdbi->ptMinSize.x = 10;		pdbi->ptMinSize.y = 10;	}	if(pdbi->dwMask & DBIM_MAXSIZE)	{	// 最大尺?(-1 表示 4G)		pdbi->ptMaxSize.x = -1;		pdbi->ptMaxSize.y = -1;	}	if(pdbi->dwMask & DBIM_INTEGRAL)	{		pdbi->ptIntegral.x = 1;		pdbi->ptIntegral.y = 1;	}	if(pdbi->dwMask & DBIM_ACTUAL)	{		pdbi->ptActual.x = 0;		pdbi->ptActual.y = 0;	}	if(pdbi->dwMask & DBIM_TITLE)	{	// H口标题		wcscpy(pdbi->wszTitle,L"H口标题");	}	if(pdbi->dwMask & DBIM_MODEFLAGS)	{		pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;	}	if(pdbi->dwMask & DBIM_BKCOLOR)	{	// 如果使用默认的背景色Q则U除该标?	pdbi->dwMask &= ~DBIM_BKCOLOR;	}	return S_OK;}		
3.3 选择实现?COM 接口
  有两个接口不是必d现的Q但也许很有用:IInputObject ?IContextMenu。如?band 对象需要接收用L输入Q那么必d?IInputObject 接口。IE 实现?IInputObjectSite 接口Q当容器中有多个输入H口Ӟ它调?IInputObject 接口Ҏ(gu)去负责管理用L输入焦点?br>在浏览器栏中需要实?个函敎ͼUIActivateIO()、HasFocusIO()、TranslateAcceleratorIO()?br>当浏览器栏激zL失去zL的时候,IE 调用 UIActivateIO 函数Q当Ȁzȝ时候,览器栏一般调?SetFocus 去设|它自己H口的焦炏V当 IE 需要判断哪个窗口有焦点的时候,它调?HasFocusIO 。当览器栏的窗口或其子H口有输入焦Ҏ(gu)Q则应返?S_OKQ否则返?S_FALSE。TranslateAcceleratorIO 允许对象处理加速键Q例子程序中没有实现Q所以直接返?S_FALSE?
STDMETHODIMP CExplorerBar::UIActivateIO(BOOL fActivate, LPMSG pMsg){    if(fActivate)        SetFocus(m_hWnd);    return S_OK;}STDMETHODIMP CExplorerBar::HasFocusIO(void){    if(m_bFocus)        return S_OK;    return S_FALSE;}STDMETHODIMP CExplorerBar::TranslateAcceleratorIO(LPMSG pMsg){    return S_FALSE;}      
  Band 对象能够通过包容器的 IOleCommandTarget::Exec() 调用执行命o。?IOleCommandTarget 接口指针Q则可以通过调用包容器的 IInputOjbectSite::QueryInterfaceQIID_IOleCommandTarget,...Q?函数得到。CGID_DeskBand 是命令组Q当一?band 对象?GetBandInfo 被调用的时候,包容器通过 dwBandID 参数指定一?ID l?band 对象Q对象要保存住这个IDQ以便调?IOleCommandTarget::Exec()的时候用。ID 的命令有Q?
  • DBID_BANDINFOCHANGED
    Band 的信息变化。设|参?pvaIn ?band IDQ??ID 是最q一ơ调?GetBandInfo 所得到的|容器会调?band 对象?GetBandInfo 函数来更新请求信息?
  • DBID_MAXIMIZEBAND
    最大化 band。设|参?pvaIn ?band IDQ该 ID 是最q一ơ调??GetBandInfo ?所得到的倹{?
  • DBID_SHOWONLY
    打开或关闭容器中其它?bands?讄参数 pvaIn 为VT_UNKNOWN cdQ它可以是如下的|
     
    ?/font> 描述
    pUnk band 对象?IUnknown 指针Q其它的桌面 bands 被隐藏
    0 隐藏所有的桌面 bands
    1 昄所有的桌面 bands

  • DBID_PUSHCHEVRON
    在菜单项左边昄“v”的选择标志。容器发送一?RB_PUSHCHEVRON 消息Q当 band 对象接收到通知消息 RBN_CHEVRONPUSHED 提示它显CZ?v"的标志。设|?IOleCommandTarget::Exec 函数?nCmdExecOpt 参数?band IDQ该 ID 是最q一ơ调?GetBandInfo ?所得到的|讄 IOleCommandTarget::Exec 函数?pvaIn 参数?VT_I4 cdQ这是应用程序定义的一个|它通过通知消息 RBN_CHEVRONPUSHED 中lAppValue 回传l?band 对象?

3.4 Band 对象注册
  Band 对象必须注册Z?OLE q程内的服务器,q且支持 apartment U程公寓。注册表中默认键的值是表示菜单的文字。对于浏览器栏,它加?IE 菜单?#8220;查看\览器栏”中;对于工具?band Q它加到 IE 菜单?#8220;查看\工具?#8221;中;对于桌面 bandQ?它加到系lQ务栏的快捯单中。在菜单资源中,可以使用“&”指明加速键?br>
通常Q一个基本的 band 对象的注册表目是:

HKEY_CLASSES_ROOT
CLSID
{你的 band 对象?CLSID}

  (Default) = 菜单的文?
  InProcServer32
   (Default) = DLL 的全路径文g?
   ThreadingModel= Apartment

工具?bands q必L它们?CLSID 注册?IE 的注册表中?br>
?HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Toolbar 下给?CLSID 作ؓ键名Q而其键值是被忽略的?br>
HKEY_LOCAL_MACHINE
Software
Microsoft
Internet Explorer
Toolbar

  {你的 band 对象?CLSID}

  q有几个可选的注册表项?例子E序q不是这样实现的)。比如,你想让浏览器栏显C?HTML 的话Q必要如下讄注册表:

HKEY_CLASSES_ROOT
CLSID
{你的 Band 对象?CLSID}
Instance
CLSID
  
(Default) = {4D5C8C2A-D075-11D0-B416-00C04FB90376}

同时Q如果要指定一个本地的 HTML 文gQ那么要如下讄Q?

HKEY_CLASSES_ROOT
CLSID
{你的 Band 对象?CLSID}
Instance
InitPropertyBag
  
Url

  另外Q还可以指定览器栏的宽和高Q当Ӟ它是依赖于这个栏是纵向还是横向的。其实这个项目无所谓,因ؓ当用戯整了览器栏的大后Q会自动保存在注册表中的?br>
HKEY_CURRENT_USER
Software
Microsoft
Internet Explorer
Explorer Bars
{你的 Band 对象?CLSID}
  
BarSize

  BarSize 键的cd必须?REG_BINARY cdQ它?个字节。左起前4个字节,是用16q制表示的像素宽度或高度Q后4个字节保留,你应该设|ؓ0。下面是一个可以在览器栏上显C?HTML 文g的全部注册表目的例子,默认宽度?91Q?x123Q个像素点:

HKEY_CLASSES_ROOT
CLSID
{你的 Band 对象?CLSID}

 (Default) = 菜单文字
 InProcServer32
  (Default) = DLL 的全路径文g?
  ThreadingModel= Apartment
Instance
CLSID

  (Default) = {4D5C8C2A-D075-11D0-B416-00C04FB90376}
InitPropertyBag
  Url= 你的 HTML 文g?br>
HKEY_CURRENT_USER
Software
Microsoft
Internet Explorer
Explorer Bars
{你的 Band 对象?CLSID}

  BarSize= 23 01 00 00 00 00 00 00

  对于注册表的讄Q用 ATL 实现其实是异常简单的。打开工程?xxx.rgs 文gQƈ手工~辑一下就可以了?下面q个文g源码Q是例子E序?IE 工具栏的注册表样式,HKLM 是需要手工添加的Q因为它不用组件类型方式注册。而对于其它类型的 band 对象只要在类声明中添加:

BEGIN_CATEGORY_MAP(Cxxx)			// 向注册表中注?COM cd	IMPLEMENTED_CATEGORY(CATID_InfoBand)	// 垂直样式的浏览器栏END_CATEGORY_MAP()		
IE 工具栏类?band 对象?#8220;.rgs”文g
HKCR	// q个目?ATL 帮你生成的,你只要手工修?#8220;菜单上的文字”可以了{	Bands.ToolBar.1 = s ''ToolBar Class''	{		CLSID = s ''{ 你的 CLSID }''	}	Bands.ToolBar = s ''ToolBar Class''	{		CLSID = s ''{ 你的 CLSID }''		CurVer = s ''Bands.ToolBar.1''	}	NoRemove CLSID	{		ForceRemove { 你的 CLSID } = s ''用在菜单上的文字(&T)''		{			ProgID = s ''Bands.ToolBar.1''			VersionIndependentProgID = s ''Bands.ToolBar''			ForceRemove ''Programmable''			InprocServer32 = s ''%MODULE%''			{				val ThreadingModel = s ''Apartment''			}			''TypeLib'' = s ''{xxxx-xxxx-xxxxxxxxxxxxxxx}''		}	}}HKLM	// q个目是手工添加的IE工具栏所Ҏ(gu)的{	Software	{		Microsoft		{			''Internet Explorer''			{				NoRemove Toolbar				{					ForceRemove val { 你的 CLSID } = s ''随便l个说明性文字串''				}			}		}	}}		
四?ATL 实现
  下蝲代码?VC 6.0 工程)Q请参照前面的说明仔l阅读,代码中也有一些关键点的注释。如果想q行Q则可以?regsvr32.exe q行注册Q然后打开 IE 览器或资源览器就可以看到效果了。如果想自己实践一下,可以按照如下的步骤构造工E:

4.1 建立一?ATL DLL 工程
4.2 d New ATL Object...Q选择 Internet Explorer ObjectQ选这个类型的目的是让向导l我们添?IObjectWithSite 的支持。如果你使用的是 .net 环境Q则不要忘记选择支持q个接口?br>


4.3 输入对象名称Q比如我惛_立一个垂直的览器栏Q不妨叫?VerBar



4.4 U程模型必须选择 ApartmentQ接口类型的选择无所谓,看你想不x?IDispatch 接口功能了。在例子E序中的垂直览器栏中,׃x单的操纵 IE 和从 IE 中接受事Ӟq接点)Q选择 Dual 是必要的。聚合选项Q你只要别选择 Only 可以了?br>


4.5 展现你无IL智慧Q开始输入程序吧。如果是 Debug 方式~译Q可能会出现一个连接错误,报告找不到_AtlAxCreateControlQ那么你要在菜单 Project\Settings...\Link 中增加对 Atl.lib 的连接。或者?#pragma comment ( lib, "atl" )加入q接库?br>4.6 如果惌试代码,在菜?Project\Settings...\Debug 中输?IE 的\径名Uͼ比如Q?#8220;C:\Program Files\Internet Explorer\IEXPLORE.EXE”Q然后就可以跟踪断点调试了?~译和调试桌面工h?band 对象Q是非常ȝ的,因ؓ计算机启动时自动q行 ShellQ?Shell ׃加蝲zd的桌面对象?br>
五、结束语
好了Q到q里Q就到这里了。祝大家学习快乐^_^ 


]]>
COM lg设计与应用(十七Q?---持箋?/title><link>http://www.shnenglu.com/mydriverc/articles/29016.html</link><dc:creator>旅?/dc:creator><author>旅?/author><pubDate>Mon, 30 Jul 2007 07:59:00 GMT</pubDate><guid>http://www.shnenglu.com/mydriverc/articles/29016.html</guid><wfw:comment>http://www.shnenglu.com/mydriverc/comments/29016.html</wfw:comment><comments>http://www.shnenglu.com/mydriverc/articles/29016.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/mydriverc/comments/commentRss/29016.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/mydriverc/services/trackbacks/29016.html</trackback:ping><description><![CDATA[作者:<a href="mailto:good_yf@sina.com"><u><font color=#0000ff>杨老师</font></u></a> <p><a ><u><font color=#0000ff>下蝲源代?/font></u></a><strong><br><br>一、前a</strong><br>  我们写程序,l常需要实现这L需求:<br>例一、程序运行生一个窗口,用户关闭的时候需要记录窗口的位置Q以便下ơ运行时保持位置不变Q?br>例二、由于程序运行时间很长,今天执行一部分Q明天l执行。那么在下次q行前要恢复前次的状态;<br>... ... ... ...<br><br>智慧的老师Q以上这些需求,如何实现呢?<br>應|的学生:q个单,只要在程序退出前提取必要的信息保存到文g中,下次q行时再从文件中d来,讄一下就O(jin)K了?br>智慧的老师Q恩Q不错,q位同学的思想值得表扬?br>應|的学生:不好意思,q都要感谢老师的栽培,我对(zhn)的景Ԓ如滔滔江?.....<br>智慧的老师Q别臭P了,我话q没有说完那......如果你需要提取和保存的信息很多,l构很复?.....怎么办?<br>應|的学生:也好办,我设计一个结构来记录q些信息?br>智慧的老师Q恩......不错。但如果q些信息提供Ҏ(gu)别h写的模块Qƈ且随着版本的不同还l常变化Q你怎么办?<br>應|的学生:... ...<strong><br></strong>智慧的老师Q解册些问题的Ҏ(gu)?--持箋性?strong><br><br>二、原?br></strong>  持箋性,也叫怹性。组件方提供 IPersistXXX 接口Q调用者(容器Q提供存储介质,比如文g啦、内存啦、注册表啦、流啦、文本啦......啦啦拉。需要保存的时候,调用者通过 IPersistXXX::Save() 接口函数让组件去自己存储属性信息,而调用者根本不用关心存储格式和存储内容Q需要还原状态的时候,调用者打开存储介质Q然后同栯?IPersistXXX::Load() 接口函数让组件自己去d属性信息ƈ完成初始化的讄?br>  目前Q微软定义了如下各种cd的持l性接口,_满你的需求了。我们只要在自己写的lg中实现其中一个或几个持箋性接口,那么调用者就可以按照l一的方式和我们的组件协商完成属性信息的保存和状态还原了?br>  <table cellSpacing=1 width="100%" border=1> <tbody> <tr> <td align=middle width="20%"><strong>持箋性接?/strong></td> <td align=middle width="80%" colSpan=2><strong>要说?/strong></td> </tr> <tr> <td width="20%">IPersist</td> <td width="80%" colSpan=2>   所有持l性接口的根,下面的接口大多从它派生出来。这个接口很单,只有一个函?GetClassID()它返回组件的 CLSID P以便调用者能保存q个号ؓ来 CoCreateInstance() 启动lg用?br>   实现q个函数也很单,只要q回你组件中?CLSID_XXX 卛_Q或者比较省事的Ҏ(gu)是返?GetObjectCLSID() ?/td> </tr> <tr> <td width="20%" rowSpan=5>IPersistStream</td> <td width="80%" colSpan=2> <p align=center>z?IPersistQƈ增加?个函敎ͼ从流(IStream)中读写组件属性信息?/p> </td> </tr> <tr> <td width="15%">IsDirty()</td> <td width="65%">lg内部属性是否发生了变化。ؓ调用者是否需要保存信息提供依?/td> </tr> <tr> <td width="15%">Load()</td> <td width="65%">?IStream 中读入信息,初始化组件属?/td> </tr> <tr> <td width="15%">Save()</td> <td width="65%">把属性信息保存到 IStream ?/td> </tr> <tr> <td width="15%">GetSizeMax()</td> <td width="65%">q回信息寸Q以便调用者事先开辟空?/td> </tr> <tr> <td width="20%">IPersistStreamInit</td> <td width="80%" colSpan=2>z?IPersistStreamQƈ再增加了一个函?InitNew() 用来完成一个默认的lg属性初始化?br>q个持箋性接口是最常用的,本文CZ中就实现了该接口?/td> </tr> <tr> <td width="20%">IPersistMemory</td> <td width="80%" colSpan=2>?IPersistStreamInit cMQ但使用的是内存块,而不是大可变化?IStream ?/td> </tr> <tr> <td width="20%">IPersistStorage</td> <td width="80%" colSpan=2>?IPersistStream cMQ但保存属性信息用的是存?IStorageQ一?IStorage 中可以有多个 IStream?/td> </tr> <tr> <td width="20%">IPersistFile</td> <td width="80%" colSpan=2>?IPersistStream cMQ但存储介质为文件?/td> </tr> <tr> <td width="20%">IPersistPropertyBag</td> <td width="80%" colSpan=2>   使用属性包Q属性名、属性|的文本方式保存信息。在 IE 览器中QHTML 嵌入 ActiveX 控g通常使用q个Ҏ(gu)?br>   ?HTML 中插入控Ӟ<param name="属性名U? value="?> q样的Ş式你应该见过吧?Q?br>   在下一回的文章中,我们介绍q个接口。因为在 ActiveX 中,它太常用了?/td> </tr> <tr> <td width="20%">IPersistPropertyBag2</td> <td width="80%" colSpan=2>扩展?IPersistPropertyBag 接口。提供了更丰富一些的属性管理用函数?/td> </tr> <tr> <td width="20%">IPersistMoniker</td> <td width="80%" colSpan=2>用于命名(moniker)存储和读取状态的持箋性接口?/td> </tr> <tr> <td width="20%">IPersistHistory</td> <td width="80%" colSpan=2>q行?IE 上,惛_用户览 WEB 面时存储和d状态的持箋性接口?/td> </tr> </tbody> </table> </p> <p><strong>三、持l性接口组件的实现</strong><br>  CZE序分别?vc6.0 ?vc.net 上实C IPersistStreamInit 接口?COM lg和调用D例。组件完成的功能是计素敎ͼ你第一ơ运行的时候,会得到第一个素?Q然后是3Q?Q?Q?1......下班旉CQ今天就q行到这里。于是调用者开辟一个流来保存组件的属性信息。明天l运行的时候,从流中原换组件状态,开始了新的计算 13Q?7Q?9Q?3......<br>  q个CZ应用完全是假设性的Q其实没有什么实用h(hun)|只是演示?IPersistStreamInit 接口的实现方法。另外,关于建立?IStream)的方法,请参?a ><u><font color=#0000ff>?/font></u></a><a target=_blank><u><font color=#0000ff>COM lg设计与应用(一Q?/font></u></a>?br><br>1、徏立一?ATL 工程目?br>2、增?ATL lgc,vc.net 使用者注意不要选择“属性化~程”方式Q其它的讄全部使用默认Ҏ(gu)。当然你愿意适当地改变选择也无所谓?br>3、设计完成你的组件功能?br>   CZE序中,实现了一个接口函?GetNext() 负责计算下一个素数?br>4、添加IPersistStreamInit 接口?/p> <pre>class ATL_NO_VTABLE Cxxx : public CComObjectRootEx<...> , public CComCoClass<...>, ...... <strong>public IPersistStreamInit // 手工d持箋性接?/strong>{......BEGIN_COM_MAP(Cxxx) ......<strong> // 手工d接口映射表入?COM_INTERFACE_ENTRY(IPersistStreamInit) // 表示如果要取?IPersistStream 指针Q则q回 IPersistStreamInit 指针 COM_INTERFACE_ENTRY_IID(IID_IPersistStream, IPersistStreamInit) // 表示如果要取?IPersist 指针Q则q回 IPersistStremInit 指针 COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersistStreamInit)</strong>END_COM_MAP()</pre> 5、完?IPersistStreamInit 接口函数?br>手工?h 头文件中增加函数声明Q? <pre>public:// IPersist STDMETHOD(GetClassID)(/*[out]*/CLSID * pClassID);// IPersistStream STDMETHOD(IsDirty)(void); STDMETHOD(Load)(/*[in]*/IStream *pStm); STDMETHOD(Save)(/*[in]*/IStream *pStm,/*[in]*/BOOL fClearDirty); STDMETHOD(GetSizeMax)(/*[out]*/ULARGE_INTEGER *pcbSize);// IPersistStreamInit STDMETHOD(InitNew)(void);</pre> 手工?cpp 文g中增加函数实玎ͼ <pre>// IPersistSTDMETHODIMP Cxxx::GetClassID(/*[out]*/CLSID * pClassID){ *pClassID = GetObjectCLSID(); return S_OK;}// IPersistStreamSTDMETHODIMP Cxxx::IsDirty(void){ if( 数据已经改变Q需要保?) return S_OK; else return S_FALSE;}STDMETHODIMP Cxxx::Load(/*[in]*/IStream *pStm){ return pStm->Read( d哪里, d长字? NULL);}STDMETHODIMP Cxxx::Save(/*[in]*/IStream *pStm,/*[in]*/BOOL fClearDirty){ if( fClearDirty ) 清除内部表示数据变化的变? return pStm->Write( 需要保存的数据指针, 写多长字? NULL );}STDMETHODIMP Cxxx::GetSizeMax(/*[out]*/ULARGE_INTEGER *pcbSize){ pcbSize->LowPart = 需要保存数据长度的低位; pcbSize->HighPart = 需要保存数据长度的高位;// 一般都?Q难道你的数据长度都过?4GQ?return S_OK;}// IPersistStreamInitSTDMETHODIMP Cxxx::InitNew(void){ 内部属性数据默认初始化; 讄或清除内部表C数据变化的变量; return S_OK;}</pre> <strong>四、小l?/strong><br>  下蝲CZE序后,l合本文仔细阅读代码Qƈ试着q行看看效果。如果你理解了,那么你能自己实现 IPersistFile 接口吗?你能自己实现 IPersistStorage 接口吗?<strong>你实现的持箋性接口越多,别h使用你的lgp方便</strong>Q也是说你的组件就能大卖特卖啦Q祝你ؓ中国软g事业做A献的同时多多赚钱:-)下回我们?IPersistPropertyBag 接口实现持箋性属性包功能Q别忘了看呦......<br> <img src ="http://www.shnenglu.com/mydriverc/aggbug/29016.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/mydriverc/" target="_blank">旅?/a> 2007-07-30 15:59 <a href="http://www.shnenglu.com/mydriverc/articles/29016.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.4src.com.cn" target="_blank">þۺ</a>| <a href="http://www.tom530.cn" target="_blank">޾Ʒþǧն</a>| <a href="http://www.ahgmxy.com.cn" target="_blank">99ƷѾþþþþ </a>| <a href="http://www.baaag.cn" target="_blank">޹뾫ƷŮ˾þþò </a>| <a href="http://www.gdcloth.cn" target="_blank">ƷþþþþӰԺ</a>| <a href="http://www.qdyshl.cn" target="_blank">þþþ߽ۺϳ</a>| <a href="http://www.lidonsj.cn" target="_blank">ձƷþþþĻ8</a>| <a href="http://www.jxjkyt.cn" target="_blank">þþƷ99þþùŴ</a>| <a href="http://www.htlon.cn" target="_blank">þˬˬAVƬ</a>| <a href="http://www.rc51.cn" target="_blank">99þþƷѹƬ</a>| <a href="http://www.3233567.cn" target="_blank">ݺɫþۺ</a>| <a href="http://www.hycv.cn" target="_blank">99þùں;Ʒ1ӳ</a>| <a href="http://www.chizhou8.cn" target="_blank">ŷƷ˿þþĻ </a>| <a href="http://www.ogus.cn" target="_blank">97þþƷˬ</a>| <a href="http://www.xinkb.cn" target="_blank">Ʒһþþ</a>| <a href="http://www.zjwazx.cn" target="_blank">þþþ99оƷ10</a>| <a href="http://www.top119.cn" target="_blank">ɫþùƷ12p </a>| <a href="http://www.wzyuan25.cn" target="_blank">þþþ޹</a>| <a href="http://www.qq577.cn" target="_blank">999þþѹƷ</a>| <a href="http://www.shiyana.cn" target="_blank">97þþƷһ</a>| <a href="http://www.k6399.cn" target="_blank">þþþAVƬ</a>| <a href="http://www.huizhiming.cn" target="_blank">ۺһ˾þþƷ</a>| <a href="http://www.panxl.cn" target="_blank">þþƷϵ</a>| <a href="http://www.rljps.cn" target="_blank">þþƷۺ</a>| <a href="http://www.obsessions.cn" target="_blank">þþþƷþþþɫӰ</a>| <a href="http://www.nbbook.com.cn" target="_blank">ƷŷþþþӰ</a>| <a href="http://www.114tmall.cn" target="_blank">Ʒžžžþþž</a>| <a href="http://www.zhifuse.cn" target="_blank">99þ뾫Ʒϵ </a>| <a href="http://www.ciidc.org.cn" target="_blank">Ʒþ8xѹۿ</a>| <a href="http://www.j19785.cn" target="_blank">˾þþƷһ</a>| <a href="http://www.zhongshengwsl.cn" target="_blank">97þþƷһ</a>| <a href="http://www.swd5.cn" target="_blank">Ʒþþ</a>| <a href="http://www.2bseo.cn" target="_blank">˾Ʒþ</a>| <a href="http://www.mt4.net.cn" target="_blank">ξþ99Ʒþþþþ</a>| <a href="http://www.dnsdna.cn" target="_blank">޹˾Ʒþþþþۺ </a>| <a href="http://www.pbti.com.cn" target="_blank">һɫۺϾþ</a>| <a href="http://www.mivrcloud.cn" target="_blank">þɬۺ</a>| <a href="http://www.4fp5r8p.cn" target="_blank">ŷպƷþ</a>| <a href="http://www.phb321.cn" target="_blank">þAVӰ</a>| <a href="http://www.enepower.cn" target="_blank">91þþƷһëƬ</a>| <a href="http://www.ehrmfye.cn" target="_blank">Ļ˾þ </a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>