| 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. |
| 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. |
| 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. |
| 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. |
| 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 |
| 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. |
| 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. |
| 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. |