com >> NewEnum and IUnknown

by Simon Woods » Sat, 25 Feb 2006 01:07:27 GMT

Hi

I'm basically wanting to intercept the For ... each mechanism to test
whether the object returned from the [_NewEnum] call fulfills a specific
criteria. If it does I want to return it, otherwise I want to recall the
[_NewEnum] until I either run out of objects or I find one with fulfills my
criteria

I'm finding that assigning an IUnknown to my own object definition is
generating a type mismatch. I thought all objects implemented IUnknown under
the hood.

Can I do it this way or will I have to implement a custom enumerator?

Here's my code


Public Property Get NewEnum() As IUnknown

Dim l_oUnknown As IUnknown
Dim l_oMyObject As CMyObject

Do
'm_cMyObjects is a VBA Collection of CMyObject
Set l_oUnknown = m_cMyObjects.[_NewEnum]

Set l_oMyObject = l_oUnknown <= generates a type mismatch

Loop While Not l_oMyObject.PassTest

Set NewEnum = l_oMyObject

End Property


Thanks

Simon




com >> NewEnum and IUnknown

by alpine » Sat, 25 Feb 2006 01:48:40 GMT


.[_NewEnum] returns a pointer to the IUnknown of an enumerator object
(an object of type IEnumVariant) which is the reason you're getting a
type mismatch.

For more detail about this whole process, you might want to have a
look at the collections stuff at http://www.mvps.org/vbvision

HTH,
Bryan
_______________________________
Bryan Stafford
New Vision Software
newvision_don' XXXX@XXXXX.COM


On Fri, 24 Feb 2006 17:07:27 -0000, "Simon Woods"






com >> NewEnum and IUnknown

by MikeD » Sat, 25 Feb 2006 06:48:00 GMT






I don't think that's a good idea. It might be possible, but I can see it
being messy. Instead, let the code which is instigating the For..Each loop
check PassTest.

IOW...

Dim MyObject As CMyObject

For Each MyObject In MyObjectCollection
If MyObject.PassTest Then
'do whatever
End If
Next

That just seems to me to be a lot cleaner than what you're trying.

--
Mike
Microsoft MVP Visual Basic





NewEnum and IUnknown

by Michael C » Mon, 27 Feb 2006 07:49:33 GMT





NewEnum is only called once and returns the enumerator. To get each object
the Next method is called on the enumerator which is the method you will
need to override. So yes you will need a custom enumerator which is probably
possible in vb with the usual hacks.

Michael




NewEnum and IUnknown

by Simon Woods » Mon, 27 Feb 2006 22:00:48 GMT







Fair enough ... I didn't really want to expose the filtering of
MyObjectCollection as I didn't think the consumer needed to know anything
about it ... but perhaps, as you say, it is the cleanest way.

Thanks to all




NewEnum and IUnknown

by Michael C » Tue, 28 Feb 2006 06:29:20 GMT




Not necessarily, it doesn't look like it's too hard to achieve what you want
to achieve.

Michael




NewEnum and IUnknown

by Simon Woods » Tue, 28 Feb 2006 17:57:30 GMT






Really?

It struck me that adding an 'if' seemed certainly simpler than adding all
the custom enumerator stuff, and that's why I thought perhaps intervening in
the NewEnum may offer a half-way house. Perhaps it's the way I'm looking at
it since I've only done custom enumeration once before, I thought there'd be
a fair bit of learning curve for me. Please feel free to show me otherwise.

Thanks

Simon




NewEnum and IUnknown

by Schmidt » Tue, 28 Feb 2006 18:28:31 GMT


"Simon Woods" < XXXX@XXXXX.COM > schrieb im



Here's an example of an IEnumVariant-Implementation:
(encapsulates a Recordset with a Lightweight-WrapperClass)
You can easily enhance this example with your own Filtering,
change the Data-Container from a Rs to a Collection, etc.
Simply debug this step by step and you will understand.

'****Into a Module
Private Declare Sub RtlMoveMemory Lib "kernel32" _
(Dest As Any, Src As Any, ByVal cb&)
Private Declare Function VariantCopy& Lib "oleaut32" _
(pvargDest, pvargSrc)

Public Type EnumVariant
vTable As Long
RefCount As Long
Index As Long
Parent As Object
End Type

Public Function SetEnum(E As EnumVariant, Parent, _
Optional StartIdx&) As IUnknown
Static IEnumVariant_vTable&(6)
E.vTable = VarPtr(IEnumVariant_vTable(0))
E.RefCount = 1: E.Index = StartIdx
Set E.Parent = Parent
RtlMoveMemory SetEnum, VarPtr(E), 4
If IEnumVariant_vTable(0) Then Exit Function
IEnumVariant_vTable(0) = AddrOf(AddressOf QueryInterface)
IEnumVariant_vTable(1) = AddrOf(AddressOf AddRef)
IEnumVariant_vTable(2) = AddrOf(AddressOf Release)
IEnumVariant_vTable(3) = AddrOf(AddressOf NextItem)
End Function

Private Function QueryInterface&(E As EnumVariant, riid&, lObj&)
E.RefCount = E.RefCount + 1
lObj = VarPtr(E)
End Function

Private Function AddRef&(E As EnumVariant)
E.RefCount = E.RefCount + 1
AddRef = E.RefCount
End Function

Private Function Release&(E As EnumVariant)
E.RefCount = E.RefCount - 1
Release = E.RefCount
If E.RefCount = 0 Then Set E.Parent = Nothing
End Function

Private Function NextItem&(E As EnumVariant, ByVal C&, V, ByVal pCFtch&)
Dim Idx&: Idx = E.Index
If Not E.Parent Is Nothing Then VariantCopy V, E.Parent.NewEnum(E.Index)
If E.Index = Idx Then NextItem = 1
End Function

Private Function AddrOf&(ByVal Addr&)
AddrOf = Addr
End Function

'*****Into a class CCustomerList
Public Rs As Object
Private Customer As New CCustomer

Public Function NewEnum(Optional Index&) '***Proc-Attr to (-4) !***
Static E As EnumVariant
'first call comes always from outside - we init only and then exit
If E.Parent Is Nothing Then Set NewEnum = SetEnum(E, Me): Exit Function

'this is the part called from inside our IEnumV-Impl. (the *.bas-Module)
'the Enumeration stops, if the index-param is not changed
Dim Fld
If Index = 0 Then Rs.MoveFirst
If Rs.EOF Then Exit Function 'means, the Index-Param stays unchanged
For Each Fld In Rs.Fields 'If Customer-PropNames = FieldNames
CallByName Customer, Fld.Name, VbLet, Fld.Value
Next
Set NewEnum = Customer
Rs.MoveNext
Index = Index + 1 'if we change the Index, then we are called again
End Function

'*****Into a class CCustomer
Public ID&, Name$

'*****Into a Testform
Private Sub Form_Click()
Dim C As CCustomer, CList As CCustomerList
Set CList = New CCustomerList
Set CList.Rs = CreateObject("adodb.Recordset")
CList.Rs.Fields.Append "ID", adInteger
CList.Rs.Fields.Append "Name", adVarWChar, 255
CList.Rs.Open
CList.Rs.AddNew Array("ID", "Name"), Array(1, "Name1")
CList.Rs.AddNew Array("ID", "Name"), Array(2, "Name2")
CList.Rs.AddNew Array("ID", "Name"), Array(3, "Name3")
For Each C In CList
Print C.ID, C.Name
Next
End Sub

Olaf




NewEnum and IUnknown

by Simon Woods » Tue, 28 Feb 2006 19:19:56 GMT






... such misplaced confidence in my abilities ;-)

Thanks very much Olaf- I shall certainly go through this.




NewEnum and IUnknown

by Simon Woods » Wed, 01 Mar 2006 18:55:47 GMT








Olaf

Well, it's working for me but I wonder if you can help a bit further as I
don't really fully understand. I really want to recursively call NewEnum
until a condition is met. Is it really just a question of incrementing the
index within the existing NewEnum function and calling NewEnum again?

Sorry if this seems a bit obvious!

Thanks

Simon




NewEnum and IUnknown

by Schmidt » Wed, 01 Mar 2006 19:28:35 GMT


"Simon Woods" < XXXX@XXXXX.COM > schrieb im


Once a For Each-Loop is started (Calling 'NewEnum' once), you
cannot intercept it anymore from outside (excepting an 'Exit For' inside
the For-Each-Loop itself - but if I understand you correctly, this is,
what you want to avoid).
The Source I've posted cannot change this behaviour of For-Each,
but it would allow the skipping of elements of the DataContainer
*inside* the Class (be it an Array, a internal Collection or a Recordset)
- but only using Flags/Conditions, that you have to set inside the Class
*before* the For-Each-Loop is entered.
Not *you* have to call NewEnum explicitely - it is called implicitely
(triggered from the 'Next'-Call in the Outside-Loop) until you decide
(from a previously set Condition), that you want to exit the endless
Looping (by simply not changing the index anymore).

Yes - and no ;-) -> you don't have to call NewEnum yourself -
it is called automatically over and over again - you only have to
decide, when to stop.

But, if you describe, what you really want to achieve (filtering,
sorting, whatever and the DataContainer, that you want to use
inside your Class - be it an array or whatever), I will send you
an appropriate example, better addressing your scenario.

Olaf




NewEnum and IUnknown

by Simon Woods » Wed, 01 Mar 2006 19:37:52 GMT






Thanks I'm sorted

Simon




NewEnum and IUnknown

by Michael C » Mon, 06 Mar 2006 06:26:21 GMT






Yikes! :-)

Michael




Similar Threads

1. Purpose of "Public Function NewEnum() As IUnknown" - Visual Basic/VB

2. NewEnum and IUnknown

Hi

I'm basically wanting to intercept the For ... each mechanism to test 
whether the object returned from the [_NewEnum] call fulfills a specific 
criteria. If it does I want to return it, otherwise I want to recall the 
[_NewEnum] until I either run out of objects or I find one with fulfills my 
criteria

I'm finding that assigning an IUnknown to my own object definition is 
generating a type mismatch. I thought all objects implemented IUnknown under 
the hood.

Can I do it this way or will I have to implement a custom enumerator?

Here's my code


Public Property Get NewEnum() As IUnknown

    Dim l_oUnknown As IUnknown
    Dim l_oMyObject As CMyObject

    Do
        'm_cMyObjects is a VBA Collection of CMyObject
        Set l_oUnknown = m_cMyObjects.[_NewEnum]

        Set l_oMyObject = l_oUnknown <= generates a type mismatch

    Loop While Not l_oMyObject.PassTest

    Set NewEnum = l_oMyObject

End Property


Thanks

Simon 


3. getting back an HREF, iUnknown, in vb.net

4. Calling a IUnknown derived COM object from VB.NET (framework 1.1 )

Hi

I have a C++ com object that exposes IStream and IPropertyBag, this is
derived from IUnknown, there is no typelib or IDispatch etc

I need to write a simple VB.NET app in VB 2003 that can create the COM
object given the CLSID, get IStream and IPropertybag from that object
then set a couple of BSTR properties, and use that IStream to read.

Can someone give me the simplest code equivalent in VB.NET to the
following in c++ (simplified for clarity)?


// {77D4C560-6A25-44af-8DA5-44FDF9B2A154}
DEFINE_GUID(CLSID_XorEncryptor, 0x77d4c560, 0x6a25, 0x44af, 0x8d,
0xa5, 0x44, 0xfd, 0xf9, 0xb2, 0xa1, 0x54);

CoInitialize(0);
CComPtr<IUnknown> pCrypt;
pCrypt.CoCreateInstance(CLSID_XorEncryptor);
CComQIPtr<IPropertyBag> pProps(pCrypt);
CComQIPtr<IStream> pCryptStream(pCrypt);

pProps->Write(...);
pProps->Write(...);

pCryptStream->Seek(...);
pCryptStream->Read(...);


Thanks in advance
Vivek

5. different between Iunknown and IDispatch In VB? - Visual Basic

6. Com interop -> IUnknown from hWnd

7. Passing IUnknown ** to a COM method from VB6

8. various objects in my VB6 project - Calling IUnknown