我的VB作品|初学指南|编程技巧|源码下载|工具控件|VBA应用|Blog|【电信线路】|【网通线路】
   位置: VB知识库 >> 源码下载 >> VB实例源码 >> 正文

  • 当前没有记录!
  • 最新调查
        你是怎样知道VB知识库的?
    通过搜索引擎
    即时通讯工具
    朋友介绍
    友情链接
    其它

      

    频道统计
    Windows Shell接口之VB实现[VB知识库-Visual Basic Knowledge base]
    Windows Shell接口之VB实现[VB知识库-Visual Basic Knowledge base]
     更新时间:2008-1-17 19:42:37  点击数:17
    【字体: 字体颜色

    (一):ICopyHook接口 
        
      Shell有很多很多的接口,不过由于种种原因,在VB里面实现起来不是那么的简单,弄的人少了,这方面的资料也自然不是很多……最可气的是,国内竟然找不到那本可以说具有指导意义的Visual   Basic   Shell   Programming卖……  
       
      前几个星期突然想到是否做个windows外壳的系列,一方面提高自己的COM水平,另一方面也充实一下这互联网的资料库,呵呵   ^_^  
       
      第1集是ICopyHook接口。ICopyHook是在系统进行文件夹和打印机操作前回调的一个具有确认效果的接口。举例来说,文件夹改名、复制、删除的时候系统就会调用这个接口。关于这个接口的其他语言的实现网上已经有很多了,而VB的却很少见,因此,关于这个接口的意义及用法,我这里就不多说了,主要谈谈我在VB中实现这个接口时的碰到的一些问题。  
       
      问题1、ICopyHook的声明  
      这个问题是没有什么小道道好走了,只能老老实实的做个idl文件,然后编译成类型库  
       
      问题2、CopyCallBack方法的返回值问题  
      CopyCallBack应该返回IDYES,IDNO或者IDCANCEL(分别对应VB中的vbYes,vbNo和vbCancel),而在VB中,COM的方法只能返回HRESULT,否则就拜拜了。解决的办法是,函数地址替换。用自己函数的地址替换掉ICopyHook接口VTable中的CopyCallBack方法的地址。  
      这个工作又碰到另外一个问题,那就是如何得到ICopyHook接口的对象指针。我们知道用ObjPtr(Me)可以得到当前类的对象指针,当我们执行下面的语句时,好玩的事情就发生了:  
       
      Dim   oICopyHookA   As   ICopyHookA  
      Set   oICopyHookA   =   Me  
       
      这里的这个Set语句就相当于在当前的类上调用了QueryInterface(IID_ICopyHookA,....),于是乎,很轻松得,我们得到了想要的东西。接下去只要把自己的函数地址写到该写的地方就OK了。  
       
      问题3、多线程问题  
      当我解决了上面两个问题后就改了文件夹个名字来验证效果,呵呵,挺好,没有什么问题。但是,当我复制、删除文件夹的时候,Explorer刷刷得就拜拜了。后来,经过一翻跟踪分析,结果发现原来问题出在多线程上。当windows进行文件夹操作的时候会使用多线程,而在每次ExitThread的时候vb运行库就会开始回收对象,因此,在第2次调用的时候,原先的地址就变成了不可访问,最终也就连累了Explorer。思考良久,然后又分析了shell32.dll,终于看到了一丝的曙光。在呼叫CopyHook对象之前,系统会生成一个包含所有CopyHook对象的链表。每次在执行前会通过判断这个链表首地址是否有效来决定是否生成对象及链表。所以,如果在调用完毕之后把链表给喀嚓掉,那么系统就会重新生成一个有效的对象及链表。呵呵,虽然这么做不是十分理想,但的确能解决这个不大不小的问题。  
       
      基本问题就是上面那么多,还有很多其他的乱七八糟的问题,以后再讲,呵呵  
       
      我提供的例子的用法是:  
      1、注册dll  
      2、双击   .reg文件,导入注册表。这个注册表文件里只包含了和文件夹相关的入口,如果要钩到打印机,可以添加下面这段  
      [HKEY_CLASSES_ROOT\Printers\Shellex\CopyHookHandlers\CopyMonitor]  
      @="{4F07BFB0-B9FC-4BF7-BA17-0343C27C8A9E}"  

    代码下载:http://210.33.90.250/download/vbsrc/vbcopyhook.rar  

    [NextPage]

    (二):自动完成接口

    在IE地址栏中输入文字,与该文字相关的候选地址列表就会出现在其下方——这就是我们早已见怪不怪的Windows自动完成。那么,我们能在自己的应用程序中实现这样的功能么?答案当然是肯定的。使用Shell   API函数SHAutoComplete,我们可以轻易办到这一点。
    SHAutoComplete函数允许用户将系统的自动完成功能绑定到任意文本框上(包括组合框上内嵌的文本框),并让用户可以选择历史(History),文件系统(File   System)和最近使用列表(MRU   List)3种候选来源。现在,你应用程序中的文本框可以表现得与IE或Windows资源管理器上的地址栏一样了。
    ……
    那这样大家满足了么?远远没有。
    很遗憾,人这种生物可不是那么容易满足的。
    “我不要系统提供的侯选来源!我要在侯选列表显示自己定义的内容!”,程序员甲激动地喊道。
    “轰隆隆~~~”,阿甲话音刚落,远处就随雷声传来一个声音回答道,“OK~~,如你所愿!”
    ……
    要在自动完成中使用自定义的候选来源难不难?不难。为啥捏?因为MSDN上写的就那么简单。
    IEnumString是自定义候选来源中必须要实现的接口。它有如下四个接口方法:
    1、 Next
    从枚举序列中获取celt个元素。如果要获取的元素个数比序列中所剩余的元素个数要多的话,则返回所有剩余的元素。pceltFetched参数将返回最后实际获取的元素个数。
    2、 Skip
    跳过下面celt个元素
    3、 Reset
    重置枚举序列到起始状态。即下次获取操作将从第1个元素开始。
    4、 Clone
    顾名思义,克隆,也就是创建一个与当前枚举序列元素相同、状态也相同的枚举器对象。
    在你自己的候选来源类中实现了这个接口就等于完成了80%的工作。接下去,只要按照伟大的MSDN的指示,一步步创建AutoComplete对象、设定候选来源就OK了。
    1、创建自动完成对象
    CoCreateInstance   CLSID_IAutoComplete,   0,   1,   IID_IAutoComplete,   oAutoComplete(i)
    2、绑定候选来源
    Call   oAutoComplete(i).Init(hTxtWnd,   MySource,   0,   0)
    3、等待奇迹出现
    到这里,一个相当简单的自定义候选来源已经可以付诸使用,一切貌似万事大吉,可以收工回家了。然而,又是一个然而,老天并没有让事情如想象的那么一帆风顺。聪明的程序员们很快会发现应用程序有时会崩溃。这又是为虾米?程序员的命运为虾米总是那么那么的坎坷捏?不怕,同志们,有句话说得好,问题和答案总是同时产生。
    VB的硬伤——多线程就是程序崩溃的根源。每一次在绑定了自动完成对象的文本框里按下按键的时候,系统就会新创建一个线程,我们的候选来源类就是在这个线程里被实例化。离开了vb主线程的怀抱,脆弱的vb类显得是那么的无助。调用任何一个涉及到线程本地存储信息的函数或语句(比如ReDim)就会让宿主程序死无全尸。
    在vb中使用多线程之所以会崩溃,全因vb的单元线程模型所累。在别的线程里调用主线程中的对象,必须要符合COM的规则,要使用列集和散集(Marshal和Unmarshal)来处理对象,创建COM调用代理。为所有的对象都搞列集和散集操作显然不是一件好玩的事情,于是,程序员乙就问道,“难道我们就没有活路了么?”答案又是积极向上而且非常和谐的。   Windows的消息机制就是一根不错的救命稻草。在其他线程中的对象使用阻塞式的Windows   消息API     SendMessage发送自定义消息到主程序,告知主程序该对象的某某方法正被调用,请主程序做些相应的反应。这种方法可以使多线程的执行方式貌似单线程一样,从而大大降低了程序代码在其他线程中发生不幸的机会。
    到这里,问题似乎都已经解决了,可是,我们又要转折一下语气,可是可是,程序还是会挂。为啥?因为我们写了”Declare   Function   SendMessage……”。VB在每个API调用结束后都会调用一次API   GetLastError,这还不要紧,要命的是它调用完这个API后还把其结果写到Err对象中。访问Err对象的这一步用到了线程本地存储信息,于是,应用程序不幸归西。对此,解决方案有二:一、使用类型库来声明API。二、不让VB做傻事。使用类型库比较稳定,但每次修改API都要重新进行编译。麻烦了点,的确是麻烦了点。所以,我决定采取第2种方案,制止VB做傻事。思路挺简单,就是不让VB调用__vbaSetSystemError函数。具体实施起来就是,在程序运行时找到函数导入表中的__vbaSetSystemError函数位置,用自己的空函数替换掉。很幸运,前段时间我写过一个拦截API的东西,里面就包含有一个用以定位PE可执行文件导入导出函数的CVBPEFnLocator类。使用它可以很轻松的完成上面这个任务。
    Public   Sub   PatchSetSystemError()
            Dim   oPEFnLocator   As   CVBPEFnLocator
            Set   oPEFnLocator   =   New   CVBPEFnLocator
           
            With   oPEFnLocator
                    .SetTargetTo   -1
                    .AutoRestore   =   False
                   
                    .LocateImportFunction   GetModuleHandle(vbNullString),   "msvbvm60.dll ",   "__vbaSetSystemError "
                    .ImpReplace   AddressOf   MySetSystemError
            End   With
           
            Set   oPEFnLocator   =   Nothing
    End   Sub  

    代码下载:http://yuan505.vicp.net/cy_filesxxx/vbsrc/autocomplete.rar


    至少要成为本站的注册会员才能下载! 注册点我!
  • 上一篇: 利用Winsock实现的聊天室和对话系统
  • 下一篇: VB托盘程序源代码
  • 发表评论   告诉好友   打印此文  收藏此页  关闭窗口  返回顶部
    热点文章
     
    推荐文章

  • 当前没有记录!
  •  
    相关文章
    网友评论:(只显示最新5条。)
    河北宝宝网河北教育网址导航VB知识库点击申请点击申请点击申请点击申请点击申请点击申请点击申请
    点击申请点击申请点击申请点击申请点击申请点击申请
    Copyright© 2008 vbkbase.com All Rights Reserved QQ群:54150844
    冀ICP备08000517号