-----------------------------------

Acquista i software ArcGIS tramite Studio A&T srl, rivenditore autorizzato dei prodotti Esri.

I migliori software GIS, il miglior supporto tecnico!

I migliori software GIS, il miglior supporto tecnico!
Azienda operante nel settore GIS dal 2001, specializzata nell’utilizzo della tecnologia ArcGIS e aderente ai programmi Esri Italia Business Network ed Esri Partner Network

-----------------------------------



sabato 10 luglio 2010

Long hot summer ...

Grandissime novità da parte di ESRI. Queste che vi elenco sono solo alcune:

- nuova veste grafica del supporto online

- ArcGIS idea dove potete 'postare' una vostra idea per i prodotti ARCGIS o dove potete promuovere una idea interessante. Il Team ArcGIS, in funzione dell'interesse e dell'utilità, valuterà se incorporarle nelle future versioni. Qui potete vedere come funziona.

- ArcGIS 10. Per maggiori dettagli seguite il link

- ArcPad 10. Qui potete scaricare il software per poterlo valutare.

- API Client versione 2.0 versione finale:
- ArcGIS for iOS. La versione finale delle API è prevista per agosto 2010. Per maggiori dettagli seguite il link

- ArcGIS.com: visita il sito per creare mappe, trovare ed utilizzare mappe, applicazioni, e strumenti, e condividere le tue mappe e le tue applicazioni con gli altri. Nel sito, troverai applicazioni per creare e condividere mappe. Troverai migliaia di mappe di base, layer specialistici, applicazioni e strumenti che puoi visualizzare ed utilizzare ed inoltre comunità di cui puoi entrare a far parte. Per maggiori dettagli seguite il link

- ArcGIS Explorer OnLine


- Disponibile il Service Pack 2 di ArcGIS 9.3.1. Per maggiori dettagli seguite il link


Buona lunga estate calda a tutti!!!!

domenica 4 luglio 2010

Rilascio delle risorse in AO

Una delle maggiori difficoltà utilizzando gli ArcObjects in ambiente .NET è capire come gestire il ciclo di vita di un oggetto.
Come ben sappiamo, il framework .NET fornisce un ambiente dove gli oggetti vengono rilasciati quando non sono più utilizzati. Questa gestione automatica della memoria risolve i due errori più comuni delle applicazioni, ossia le perdite di memoria e i riferimenti a memoria non validi.
Questo comunque non ci esime dal prestare particolare attenzione quando abbiamo a che fare con risorse.
Gli ArcObjects sono oggetti COM (codice non gestito) che vengono 'consumati' in ambiente .NET (codice gestito). In sintesi il runtime .NET fornisce delle classi wrapper che fanno da 'ponte' tra il codice gestito (.NET) ed il codice non gestito (COM). Quando si fa riferimento a un oggetto COM, il runtime crea un Runtime Callable Wrapper (RCW) che gestisce il marshalling tra i due ambienti. In modo simile il runtime .NET crea un COM-callable wrappers (CCWs) per la comunicazione da COM a .NET.

Per generare l'RCW viene utilizzato tlbimp.exe (Type Library Importer utility) contenuto nell'SDK di .NET.
Quando facciamo riferimento ad un componente COM in Visual Studio il tlbimp.exe viene eseguito in background. Per identificare un RCW possiamo vedere in debug gli oggetti visualizzati come System.__ComObject.
Con la Type Library Importer utility le COM coClass sono convertite in classi gestite. Il nome delle classi gestite è uguale a quello di quelle non gestite con il suffisso Class. Per esempio l'RCW della classe Envelope è EnvelopeClass.

Nell'ambiente COM, la gestione della durata avviene tramite il conteggio dei riferimenti. Ogni oggetto COM dispone di un conteggio interno dei riferimenti client che lo indicano. Questi conteggi sono aumentati e diminuiti dallo sviluppatore client richiamando IUnknown::AddRef e IUnknown::Release. AddRef incrementa il conteggio e deve essere richiamato quando viene impostato un riferimento al componente, mentre Release diminuisce il conteggio e deve essere richiamato quando il riferimento viene eliminato. Se il conteggio scende a 0, l'oggetto può essere eliminato. (vedi Reference counting )
In ambiente .NET il livello utilizzato è quello di classe e l'impatto sulle prestazioni sarebbe troppo grande se il conteggio dei riferimenti fosse stato aggiunto al CLR. (vedi Garbage Collection )
In ambiente .NET l'oggetto dura finché il Garbage Collector vede che non è più raggiungibile da un riferimento principale forte tramite un percorso di riferimenti forti.

Ritorniamo alla nostra class RCW: AddRef viene chiamato dopo la creazione del componente, mentre Release viene richiamato quando l'RCW è finalizzato.

Il problema è che non sappiamo quando la finalizzazione avviene in ambiente .NET.
Il problema principale è che, se il nostro oggetto trattiene risorse, potrebbe bloccarle e quindi non renderle disponibili.

Gli ambienti .NET ed ESRI mettono a disposizione classi e metodi per una finalizzazione deterministica.
Occorre però prestare molta attenzione a quando utilizzare o meno queste classi/metodi.
Innanzitutto in ambiente .NET abbiamo a disposizione l'interfaccia IDisposable che ci permette, implementandola nella nostra classe, di rilasciare risorse gestite e non. Occorrerà seguire un determinato pattern di sviluppo poichè, se non si dovesse chiamare il metodo Dispose esplicitamente, il Garbage Collector da solo finalizzerà l'oggetto non conoscendo l'interfaccia IDisposable.

Implementando l'interfaccia IDisposable, sarà possibile utilizzare lo using che ne garantirà un utilizzo corretto.

ESRI mette a disposizione la classe ComReleaser che implementa IDisposable

Come visto nel post sul ComReleaser possiamo utilizzare questa classe per finalizzare le risorse in un determinato blocco. In sostanza ESRI ha creato un helper nel quale indichiamo quali sono gli oggetti che desideriamo rilasciare in modo deterministico.

In .NET abbiamo a disposizione il ReleaseComObject e il FinalReleaseComObject.
Il ReleaseComObject decrementa il conteggio dell' RCW associato all'oggetto COM e restituisce il valore del conteggio mentre il FinalReleaseComObject rilascia tutti i riferimenti portando il conteggio a 0, se il rilascio ha avuto successo. Occorre tener presente che l'RCW potrebbe ancora esistere perchè attende il garbage collector.

Occorre prestare attenzione a non utilizzare un COM dopo che è stato separato dal suo RCW, altrimenti si incorre in un eccezione. In questo esempio rilasciamo la FeatureClass table1 e poi successivamente la richiamiamo.

m_AOLicenseInitializer.InitializeApplication(new esriLicenseProductCode[] { esriLicenseProductCode.esriLicenseProductCodeEngine },new esriLicenseExtensionCode[] { });

            try
            {

                IPropertySet propertySet = new PropertySetClass();
                propertySet.SetProperty("DATABASE", @"D:\Temp\test.mdb");
                Type factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.AccessWorkspaceFactory");
                IWorkspaceFactory workspaceFactory = (IWorkspaceFactory) Activator.CreateInstance(factoryType);
                IWorkspace workspace = workspaceFactory.Open(propertySet, 0);
                IFeatureWorkspace featureWorkspace = workspace as IFeatureWorkspace;
                IFeatureClass table1 = featureWorkspace.OpenFeatureClass("streets");

                int k = Marshal.ReleaseComObject(table1);

                Console.Write(table1.HasOID);


                Console.Read();

            }
            catch (Exception ex)
            {
                Console.Write(ex.Message);
                Console.Read();
            }


            m_AOLicenseInitializer.ShutdownApplication();




In .NET è presente il metodo Collect che forza il garbage collector. Questo metodo è da utilizzare esclusivamente solo quando necessario (sono veramente pochi i casi). Nel caso foste interessati, vi consiglio di dare un occhio a questo post per verificare quando utilizzarlo. Nella stessa classe (GC) possiamo notare anche i metodi SuppressFinalize (da utilizzare quando ad esempio implementiamo l'IDisposable e, poichè gestiamo le risorse, desideramo evitare che il garbage collector richiami la finalizzazione sull'oggetto). WaitForPendingFinalizers è un altro metodo che permette di rimanere in attesa sul thread sul quale lo si chiama fino a quando il thread che esegue la finalizzazione degli oggetti disponibili alla finalizzazione ha terminato.


In questo esempio vediamo come i datasets sono pooled nel loro workspace

IPropertySet propertySet = new PropertySetClass();
                propertySet.SetProperty("DATABASE", @"D:\Temp\test.mdb");
                Type factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.AccessWorkspaceFactory");
                IWorkspaceFactory workspaceFactory = (IWorkspaceFactory) Activator.CreateInstance(factoryType);
                IWorkspace workspace = workspaceFactory.Open(propertySet, 0);
                IFeatureWorkspace featureWorkspace = workspace as IFeatureWorkspace;

                using (ComReleaser comReleaser = new ComReleaser())
                {
                    ITable table1 = featureWorkspace.OpenTable("zTest");
                    ITable table2 = featureWorkspace.OpenTable("zTest");


                    if (object.ReferenceEquals(table1, table2))
                       Console.WriteLine("Stesso oggetto");

                    Console.WriteLine(Marshal.FinalReleaseComObject(table1));

                    Console.WriteLine(Marshal.ReleaseComObject(table2));
                }

table1 e table2 puntano al riferimento della stessa istanza e, se eseguiamo un FinalReleaseComObject, verranno azzerati tutti riferimenti (vedi il ReleaseComObject successivo che restituisce -1) pertanto il table2 non è utilizzabile
altrimenti si incorre nell'eccezione vista precedentemente.

Come abbiamo detto nel post sul ComReleaser è importante rilasciare i cursori per evitare di lasciare in blocco la tabella alla quale il cursore fa riferimento.

                using (ComReleaser comReleaser = new ComReleaser())
                {
                    ICursor cursor = table1.Search(null, true);
                    comReleaser.ManageLifetime(cursor);
                    IRow row = null;
                    bool isManaged = false;
                    while ((row = cursor.NextRow()) != null)
                    {
                        if (!isManaged)
                        {
                            comReleaser.ManageLifetime(row);
                            isManaged = true;
                        }

                        // utilizza la row...
                    }
                }

In questo esempio abbiamo un cursore che viene riciclato (parametro true nel Search) e quindi non viene creata una nuova istanza di row ad ogni riga successiva. Pertanto occorre aggiungere la row una sola volta.
Diversamente, se dovessimo utilizzare un cursore non riciclato (parametro false nel Search) occorrerà ad ogni riga successiva gestire il rilascio poichè la row è una nuova istanza.

                using (ComReleaser comReleaser = new ComReleaser())
                {
                    ICursor cursor = table1.Search(null, false);
                    comReleaser.ManageLifetime(cursor);
                    IRow row = null;
                    while ((row = cursor.NextRow()) != null)
                    {
                        try
                        {
                            // utilizza la row...
                        }
                        catch (Exception ex)
                        {
                            // gestisci l'eccezione...
                        }
                        finally
                        {
                            Marshal.ReleaseComObject(row);
                        }
                    }
                }

Pertanto occorre sempre capire quando siamo in presenza di una stessa istanza.
Ad esempio, se ci dovessimo connettere ad ArcSDE in due modi diversi: prima con la connection properties e poi con la connection file con stessa user/password e versione, avremo un singolo COM che sarà condiviso in un singolo RCW.  Nel dubbio verificate con un object.ReferenceEquals(obj1,obj2).