由于VB不能直接支持IEnumVARIANT,
所以,我们不能编制自己的NewEnum方法,这样就失去了很多灵活性
(比如,反向的浏览集合)
偶尔看见一本VB5核心技术的东东,里面就说到用VB代码实现的IEnumVARIANT
但由于时间的关系,其具体代码EnumVar.Bas EnumVar.cls的链接已经失效了。
还请哪位有此代码的老大讲解一下如何实现本主题,谢谢!
(当然,请不要说用VC编一个什么Dll动态库的方法,偶问的是VB,偶只关心用VB的方法——例外:如果是tlb库,那到是可行的)

解决方案 »

  1.   

    [
      odl,
      uuid(00020404-0000-0000-C000-000000000046)
    ]
    interface IEnumVARIANT : IUnknown {
        long _stdcall Next(
                        [in] long celt, 
                        [in, out] VARIANT* rgvar, 
                        [in, out, optional, defaultvalue(0)] long* pceltFetched);
        HRESULT _stdcall Skip([in] long celt);
        HRESULT _stdcall Reset();
        HRESULT _stdcall Clone([out] IEnumVARIANT** ppenum);
    };
      

  2.   

    可以看看这里:
    http://www.mvps.org/vbvision/Super_Collections.htm
      

  3.   

    是这本书:
    《Visual Basic 5.0 核心技术》(原名《Hardcore Visual Basic version 5.0》),Bruce McKinney著,希望图书创作室改编。北京希望电脑公司出版,1998
      

  4.   

    4.5  Collection工作方法For Each句法对在Collection中的迭代提供了一种自然的方法。你不必担心Collection是如何组织的,或它是做什么的。For Each在循环中游历,你只要走到下一个条目。Collection的设计者决定那意味着什么。
    关于Visual Basic的Collection类工作方式没有什么神圣的东西。如果想把Collection设计成反向迭代,或是对每次迭代都产生一个随机数,这完全取决于你。问题是,Visual Basic没有为使你的Collection用For Each工作提供方便的方法。但假如理解了Collection迭代是如何工作的,就能办到不可能办到的事(或至少是不切实际的事)。你必得要理解有关IEnumVARIANT接口的一些事实。组件对象模型把IEnumVARIANT定义为在数据结构中迭代的标准方式。任何想允许客户遍历Variant数据的Collection类都应该提供一个实现IEnumVARIANT的迭代器。任何想迭代经过变量数据类(符合标准)的客户程序都可通过调用IEnumVARIANT方法来做到。
    Visual Basic的For Each句法就是这样工作的。当写下一个For Each代码块时,Visual Basic为每个迭代调用IEnumVARIANT的Next方法。Collection类可用For Each操作,是因为它提供了一个实现IEnumVARIANT的方法的迭代器类。那么,你是如何实现IEnumVARIANT的呢?困难重重。
    4.5.1  委派Collection
    Collection类创建一个帮助器类来实现IEnumVARIANT接口。要创建自己的Collection,可把数据放进你的类内的一个真正的Collection中,并令其对公众适用,就如同它是你的一样。这项技术在Visual Basic 4中适用,但只是以一种间接的、不安全的方式,把封装性置之不理。第5版纠正了那些问题,但却是以一种……方式,好了,你自己判断是什么方式吧。CDrives集合,第1版
    可使用第三章中的CDrives类来创建一个CDrives集合类。注意命名规则:类集合(collectionlike)类应该总是复数形式。图4.2显示了CDrives集合及其与CDrives类之间的关系。但在我们进入真正的CDrives类之前,来看一下我的第1个实现程序,我把它存在CDrivesO的名下。
    下面的代码(摘自TCOLLECT.FRM)显示了CDrives和CDrivesObject几乎是多态的类,可使用同一代码调用:s=s& "Drive information for available drives:" & sCrLf
    Dim drives As Object, drive As CDriveIf chkOld Then
    Set drives= New CDrives0
    Else
    Set drives=New CDrives
    EndIfFor Each drive In drives
    With drives=s& "Drive" & .Root & "[" &.Label & ":" & _
    .Serial & "](" & .KindStr & ") has " & _
    Format$(.TotalBytes, sBFormat)& sCrLf
    End With
    Next代表内部Collection的类中的大部分工作是在Class_Initialize事件过程中完成的。它在CDrivesO中是这样工作的:Private drives As New CollectionPrivate Sub Class_Initialize( )
    Refresh
    End Sub'Argument handy for refreshing local and/or remote, but not floppies
    Public Sub Refresh(Optional iFirst As Integer=1)
    Dim i As Integer, af As Long, sRoot As String
    Dim drive As CDrive' Remove old ones
    Do While drives.Count> iFirst
    drives.Remove iFirst
    Loop
    ' Insert new
    af = GetLogicalDrives( )
    For i = iFirst To 26
    If RShiftDWord(af, i-1) And 1 Then
    Set drive = New CDrive
    drive.Root = i
    drives.Add drive, drive.Root
    End If
    Next
    End SubClass_Initialize子程序只是调用Refresh来做真正的工作。在有时需要越过自动初始化的类中,从初始化事件中调用一个公共初始化方法是一项常用的技术,Refresh使用Win32 GetLogicalDrives函数和RshiftDWord函数(在第五章中讲述),来计算在系统中确实存在哪些驱动器。然后,这些驱动器被初始化,并加进内部Collection中。对老版本,Refresh方法非常重要且有用,但你不久就会看到,在现今按需要计算驱动器的真正的CDrives类中,那已成为历史。
    有了适当的内部Collection,就可以简单地实现Collection的标准属性和方法了。通过按以下方法传输内部Collection的Count和Item揭示Count和Item属性:Public Property Get Count( ) As Integer
    Count=drives.Count
    End Property'Default property
    Public Property Get Item(v As Variant) As CDrive
    'Return default (Nothing) if error
    On Error Resume Next
    Set Item=drives(v)
    End Property在类中嵌入Collection对象,并用相似的外部成员来传输其成员,是委派的另一种例子。在一般的面向对象语言中,你会用继承性来达到相同的目的。尤其是从Collection类中衍生出CDrivesO类。不必写任何代码即能取得Count和Item属性。不必为想要的任何附加成员(比如Refresh)编写代码,并且还可以解除或增强想要削弱或改变的任何成员(比如Add和Remove)的功能。挑战
    乍看,你可能会认为实现Add和remove属性是不可能的。如果可以仅仅通过调用Add方法对系统添加新的驱动器,就永远不会用完磁盘空间,但即使是即插即用标准也不能保证这一点。另一方面,使用Add连接到网络驱动器和使用Remove方法断开都很容易。你可以查阅WNetAddConnection2和WNetCancelConnection2 API函数。我将增强CDrive集合来使之完全可适用于网络的任务留给了你。委派迭代器
    只再需要一步就可使CDrivesO成为一个真正的Collection,这个步骤如此稀奇古怪,使我难以描述。但我将尽力而为。
    如果是用其他语言编写集合,就必须创建一个名为NewEnum的方法。当某程序员使用你的集合类来编码一个For Each块时,Visual Basic调用这个_NewEnum方法来创建一个隐葳的、实现IEnumVARIANT接口的迭代器对象。这个对象实质上像我们早先用CListWalker类创建的迭代器。对于每一次循环,该迭代器对象的Next方法即取得Collection中的下一个条目。
    为使你能让Visual Basic用For Each来使用你的集合,则要给你的集合一个_NewEnum,它会把其对列表对象的创建委派给内部集合。这说起来容易,做起来却不那么简单,且显示出来要比解释它更简单。' NewEnum must have the procedure ID -4 in Procedure Attributes dialog
    Public Function NewEnum( ) As IEnumVARIANT
    set NewEnum=drives.[_NewEnum]
    End Function这个方法的第一个特色是它被命名为NewEnum,而不是_NewEnum,这是因为下划线做为Visual Basic符号名称的第一个字母是非法的。第二个特色是,无论是否合法,这个Collection类都有一个_NewEnum方法,并且,你必须要将它所做的一切委派给NewEnum属性。Visual Basic最含糊的特性之一是可以通过把非法名称放进方括号中来访问。第三个特色是NewEnum返回一个IEnumVARIANT类型。这是Collection类中迭代器的类型(其他书中有些例子使用IUnknown做为返回类型,但它们是一回事)。
    最后一个也是最显著的特色是你如何告诉Visual Basic你的NewEnum方法实际上就是_NewEnum方法。这是一个简单的窍门,你把蝾螈的眼睛、青蛙的脚趾、蝙蝠的绒毛以及狗的舌头混在一起……不,等一等。那是一种不同的魔术。在这种情况下,给“来自地狱的对话框”(DBFH)中的过程ID指派一个魔数。图4.3显示了这种方法。
    你可能记得,DBFH的过程ID复合框中有几个条目,包括(None)、(Default),AboutBox和一堆不相关的废话。但它对于NewEnum、_NewEnum,或任何与你想匹配的名称却没有条目。幸好,这个过程ID值是用一个普通的复合框设置的,它允许你或是从建议的条目中选出一个,或是敲入你自己的。这就是你要做的:敲入魔数ID号,请连击-4。评论
    这个魔数起作用,虽然它违反了Visual Basic的所有标准,这是公式化的东西,没有创造性的余地。计算机比人类更善于按公式行事。Visual Basic努力想让事情更为简单。你应能够只是单击复选框就可表明你希望这个类成为一个集合。然后,可以选出想委派给哪个内部Collection变量(在不可能的事件中有一个以上的内部Collection)。Visual Basic会自动创建NewEnum属性。它不会像继承性那样简单,但至少它不是一个受嘲弄的对象。
      

  5.   

    关于IEnumVARIANT更精彩的讨论请看Matthcw Curland写的《Advanced Visual Basic》
    作者介绍了很多COM底层细节
    演示了很多原本被认为是VB办不到的功能此书书评:
    http://dev.csdn.net/develop/article/13/13308.shtm
      

  6.   

    http://www.lihuasoft.net/book/show.php?id=501Visual Basic 5.0 核心技术  
     发表日期:2005-09-19 文件大小:9.7MB 下载次数:39   
    版权所有:
    软件版本:
    文件大小:9.7MB
    操作系统:win9x/NT/2000/XP
    下载说明: Visual Basic 5.0 核心技术 
      

  7.   

    To rainstormmaster(暴风雨 v2.0)老大,你这个通不过啊![
      odl,
      uuid(00020404-0000-0000-C000-000000000046)
    ]
    interface IEnumVARIANT : IUnknown {
        long _stdcall Next(
                        [in] long celt, 
                        [in, out] VARIANT* rgvar, 
                        [in, out, optional, defaultvalue(0)] long* pceltFetched);
        HRESULT _stdcall Skip([in] long celt);
        HRESULT _stdcall Reset();
        HRESULT _stdcall Clone([out] IEnumVARIANT** ppenum);
    };
      

  8.   

    提示在这行:
    interface IEnumVARIANT : IUnknown {消息是:
    missing 'uuid' attribute
    SuperCollection 的那个我看了,但我也想知道一下,自己来定义odl
    (我才不要它那么多接口呢,偶只要简单点,像Collection 一样就行)