Logo UGIdotNET

Discussione 'IEnumVARIANT da .NET'

# Pubblicato il 29 ago 2002 20.22 - Rispondi
Ettore Messina
IEnumVARIANT da .NET
Ho un grosso problema che non sono riuscito a risolvere.

Ho scritto una classe in VB.NET utilizzabile da VB6 tramite COM. Questa cosa e' abbastanza semplice e funziona tutto bene.
Ho incontrato una difficolta' ad implementare il supporto per il For Each.

Sappiamo che per supportare il For Each da .NET e' sufficiente implementare IEnumerable (e poi un enumeratore che implementa IEnumerator, bla bla bla).
Sappiamo che per effettuare un For Each da VB6, l'oggetto deve supportare i metodi Count, Item e _NewEnum. _NewEnum ritorna un oggetto COM che implementa l'interfaccia IEnumVARIANT (che, nel namespace InteropService, si chiama UCOMIEnumVARIANT).
Io ho fatto tutto questo (almeno credo di averlo fatto secondo il "regolamento"), ma non sono riuscito a cavarne le gambe.

Qualcuno sa come si fa? Quancuno sa dove reperire qualche piccolo esempio che faccia cio'? Su MSDN non c'e' quasi niente sull'argomento.

Ciao.
Grazie.
Ettore.
# Pubblicato il 30 ago 2002 9.04 - Rispondi
Marco Barzaghi
Re: IEnumVARIANT da .NET
ciao Ettore, che problemi ti da?
se non è un problema potresti postare il codice incriminato così da poter inquadrare meglio il problema.

ciao
M.
# Pubblicato il 01 set 2002 21.57 - Rispondi
Ettore Messina
Re: IEnumVARIANT da .NET
Ok. Marco.
Posto il codice incriminato.

Questo file deve far parte di una dll in VB.NET.

----------------------------------
Imports Microsoft.VisualBasic
Imports System.Runtime.InteropServices

<ComClass(MyEnumerator.ClassId, MyEnumerator.InterfaceId, MyEnumerator.EventsId)> _
Public Class MyEnumerator
Implements UCOMIEnumVARIANT

#Region "COM GUIDs"
Public Const ClassId As String = "01B73D24-55F1-425F-8935-3D1C3058D231"
Public Const InterfaceId As String = "B5E787EA-FB46-489E-8473-972BEBFDC231"
Public Const EventsId As String = "16C3D549-399F-4007-BFF5-34E282543651"
#End Region

Private m_Index As Integer
Private m_o As SupportForEach

Friend Sub New(ByVal o As SupportForEach)
m_o = o
End Sub

Public Sub Clone(ByVal ppenum As Integer) Implements System.Runtime.InteropServices.UCOMIEnumVARIANT.Clone
'....
End Sub

Public Function [Next](ByVal celt As Integer, ByVal rgvar As Integer, ByVal pceltFetched As Integer) As Integer Implements System.Runtime.InteropServices.UCOMIEnumVARIANT.Next
Return 0 'S_OK
End Function

Public Function Reset() As Integer Implements System.Runtime.InteropServices.UCOMIEnumVARIANT.Reset
m_Index = 0
Return 0 'S_OK
End Function

Public Function Skip(ByVal celt As Integer) As Integer Implements System.Runtime.InteropServices.UCOMIEnumVARIANT.Skip
Return 0 'S_OK
End Function
End Class


<ComClass(SupportForEach.ClassId, SupportForEach.InterfaceId, SupportForEach.EventsId)> _
Public Class SupportForEach
Inherits ArrayList

#Region "COM GUIDs"
Public Const ClassId As String = "01B73D24-55F1-425F-8935-3D1C3058D230"
Public Const InterfaceId As String = "B5E787EA-FB46-489E-8473-972BEBFDC230"
Public Const EventsId As String = "16C3D549-399F-4007-BFF5-34E282543650"
#End Region

Public Sub New()

End Sub

Public Overrides ReadOnly Property Count() As Integer
Get
Return MyBase.Count
End Get
End Property

Default Public Shadows ReadOnly Property Item(ByVal Index As Integer) As Object
Get
Return MyBase.Item(Index)
End Get
End Property

Public Function [_NewEnum]() As <MarshalAs(UnmanagedType.IUnknown)> Object
Return New MyEnumerator(Me)
End Function
End Class
----------------------------------


Quello che segue e' invece il test in VB6:
Private Sub Command1_Click()
Dim list As FETest2.SupportForEach
Set list = New FETest2.SupportForEach
Dim o As Object
For Each o In list
Next o
End Sub

La dll (ooprtunamente registrata e quant'altro) viene regolarmente caricata, la chiamata di altri metodi esposti (che nel codice postato per semplicita' non ci sono) riesce perfettamente. Il for each invece no. L'errore che VB6 mi presenta e': Object doesn't support this property or method.

Mi rendo conto, che la classe MyEnumerator non e' correttamente implementata, per cui non mi aspetto che il for each funzioni, ma l'errore "Object doesn't support this property or method" mi lascia perplesso. Comunque, sembra, avvenire prima che MyEnumerator entri in funzione.

Mi date una mano a capire dova sta l'inghippo.

Grazie.
Ettore.
# Pubblicato il 02 set 2002 10.43 - Rispondi
Marco Barzaghi
Re: IEnumVARIANT da .NET
La procedura _NewEnum dovrebbe essere compilata con l'attributo
Procedure ID enter -4, DispID of -4 (DISPID_NEWENUM). Putroppo in questo momento non dispongo di na macchian con .NET per fare delle prove.

Si potrebbe provare a compilare come segue...
ma su quello che segue putroppo non posso testare.. mi spiace.
spero che per lo meno ti abbbia dato idea di dove dare un occhio.

<AttributeUsage(AttributeTargets.Enum)>
Public Function [_NewEnum]() As <MarshalAs(UnmanagedType.IUnknown)> Object
Return New MyEnumerator(Me)
End Function
End Class

ciao
M.
# Pubblicato il 03 set 2002 1.32 - Rispondi
WIlliam Franchini
Re: IEnumVARIANT da .NET [lungo]
on 1. Sep 2002 21:57 Ettore Messina wrote:
> Ok. Marco.
> Posto il codice incriminato.
>
> Questo file deve far parte di una dll in VB.NET.
>
> ----------------------------------
> Imports Microsoft.VisualBasic
> Imports System.Runtime.InteropServices
>
[snip]
Forse sono io che non ho capito il problema, tuttavia mi sembra che si stiano complicando troppo le cose :-). Le collection in .NET si realizzano implementando IEnumerable. E questa è -l'unica- cosa da fare anche quando si vuole esporre una struttura di questo tipo verso COM. Il runtime fa il resto. In altre parole ogni volta che COM richiede un'istanza di IEnumVARIANT (ovvero ogni volta che si usa For each) il CCW intercetta questa richiesta e "simula" IEnumVARIANT tramite l'implementazione di IEnumerable. Più facile di così...:-D. Aggiungo un esempio.
Una classe Car:
Imports Microsoft.VisualBasic
Imports System
Imports System.Runtime.InteropServices
<ComClass()> _
Public Class Car
Private name As String
Private color As String
Public Sub New()
name = "?"
color = "-"
End Sub
Public Sub New(ByVal n As String, ByVal c As String)
name = n
color = c
End Sub
Public Overrides Function ToString() As String
Return String.Format("La macchina {0} è {1}", name, color)
End Function
End Class

Ed una collection "strong typed" per contenere delle Car (non completa di tutto...ho messo solo Add())
<ComClass()> _
Public Class CarCollection
Implements IEnumerable
Public Sub New()
carList.Add(New Car("Maggiolino", "Rosso"))
carList.Add(New Car("Torpedo", "Blu"))
End Sub
Public Sub AddCar(ByVal c As Car)
carList.Add(c)
End Sub
Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return carList.GetEnumerator()
End Function
Private carList As New ArrayList()
End Class

Compilate con uno strong name, usate regasms e gacutil e servite calda con un client VB 6.0:

Private Sub Command1_Click()
Dim cc As New EnumLib.CarCollection
Dim c As Car
For Each c In cc
MsgBox c.ToString
Next
End Sub
Funziona perfettamente...
Con OleView si può notare come l'IDL generata per CarCollection sia già adattata alla "simulazione" con IEnumVARIANT:
interface _CarCollection : IDispatch {
[id(0x00000001)]
HRESULT AddCar([in] _Car* c);
[id(0xfffffffc)]
HRESULT GetEnumerator([out, retval] IEnumVARIANT** pRetVal);
};

Notare l'ID -4 ed il fatto che GetEnumerator ritorni in effetti un IEnumVARIANT
spero aiuti
ciao William



# Pubblicato il 02 set 2002 11.01 - Rispondi
Marco Barzaghi
Re: IEnumVARIANT da .NET: errata corrige
spiacente... AttributeTargets.Enum è per gli enumeratori... mi sa DispIdAttribute è meglio

<DispIdAttribute(-4)> _
Public Function [_NewEnum]() As <MarshalAs(UnmanagedType.IUnknown)> Object
Return New MyEnumerator(Me)
End Function
End Class

ciao
M.
# Pubblicato il 02 set 2002 13.34 - Rispondi
Ettore Messina
Problema con il valore -4
Purtroppo il programma non compila nemmeno perche' il DispId -4 non lo accetta; il compilatore dice che i valori negativi di DispID sono riservati.

Grazie lo stesso del suggerimento.

Ho provato a cercare su Internet se qualcuno aveva gia' incontrato questo problema, ma... niente.

Ciao.
Ettore.
# Pubblicato il 03 set 2002 16.23 - Rispondi
Ettore Messina
Re: IEnumVARIANT da .NET [lungo]
A questa soluzione non avevo pensato.
Ad essere sincero, non pensavo che il compilatore fosse cosi' signore da implementare IEnumVariant automaticamente a fronte di una IEnumerable. Per questo pretendevo di implementare IEnumVariant a manina.

Invece questi compilatori .NET (VB, C# e C++) sono dei gran signori.
Ho lasciato perdere la IEnumVariant e ho implementato la IEnumerable, secondo le regole di .NET. Ho aggiunto l'alltributo ComClass e ... zac ... il for each fa VB6 funziona.

Grazie, William, per la dritta. Un grazie anche a Marco per l'aiuto sulla _NewEnum.

A proposito, una curiosita'.
Implementando il for each con IEnumerable, dall'object browser di VB6 non vedo _NewEnum (come mi sarei aspettato) ma vedo il metodo GetEnumerator() As IEnumVARIANT.
Premetto che non me ne importa niente, visto che il for each funziona lo stesso. Evidentemente il for each del VB6 va a chiamare, se c'e', GetEnumerator(). Non ho mai letto cio', ho sempre letto che _NewEnum fosse necessaria. Ma non vedo altre spiegazioni. Il tipo di ritorno di questo metodo, IEnumVARIANT, mi piace tantissimo. Comunque non importa.

Per rincarare il mio apprezzamento sui compilatori .NET, ho gradito il fatto che per supportare l'interfaccia IClassFactory (e la sua implementazione standard) basta definire il costruttore di default.

Mi vengono in mente i tempi di "Inside OLE". Gran libro, per carita'. Ma erano tempi da notti insonni e litri di caffe' endovena, per gli sviluppatori OLE.

Grazie ancora.
Ettore.

© 2001 User Group Italiano UGIdotNET. Tutti i diritti riservati. Note legali. - Partita IVA 01927050185