Form che si sfasciano

di Catafirro il
16 risposte

Molto tempo fa avevo chiesto aiuto per un problema di form che si corrompono dopo la chiusura senza apparente motivo in un grosso applicativo access. 

https://www.iprogrammatori.it/forum-programmazione/access/form-che-si-sfasciano-t54170.html

Si trattava solo di form in multiistanza, quelle classiche aperte con DoCmd.OpenForm non avevano problemi. Dopo aver adottato due accorgimenti per minimizzare moltissimo il problema (prima istanza aperta con DoCmd, sia in quella sia in una eventuale seconda intercettazione dell'evento Form_Close dove, invece di chiudere, rendo solo invisibile, quindi a successiva richiesta rendo di nuovo visibile, però con la terza pur rara il problema tornava) ho finalmente scoperto qual'era il problema, quindi lo comunico a chi eventualmente vi fosse incappato o v'incappasse.

Il problema era che eliminavo l'oggetto nella collection delle form multiistanza dentro l'evento Form_Close della form stessa. Questo metodo non aveva mai dato problermi per 20 anni sulla vecchia versione dell'applicativo che interfacciava un backend Access, in questo che interfaccia un db MySql invece sì. Ho eliminato il problema delegando la distruzione dell'oggetto a una routine apposita che viene chiamata sempre a ogni nuova apertura di maschera (SysMaint) che fa tante cose, come controllo di versioni, conflitti per utenti entrati due volte, pulizia di tabelle temporanee access ecc... In questa routine scorro la collection verificando la validità dell'oggetto, se l'oggetto non è più valido, quindi la form è stata chiusa, lo elimino.

Ho deciso comunque di tenere attivi i due espedienti che ho descritto poiché usare il DoCmd alla prima apertura (che potrebbe rimanere la sola) Access viene stressato di meno, e il nascondere la form invece di chiuderla rende più veloce la successiva riapertura. In sostanza chiudo solo dalla terza in poi. Pericolo di troppe maschere attive non ce n'è poiché consento le aperture multiistanza solo a poche.

Colgo anche l'occasione per un bilancio del mio metodo di usare tabelle temporanee access da linkare alle form invece della tabelle MySql, sul quale c'è stato molto scetticismo. Assicuro che dopo un anno e mezzo di funzionamento con clienti da una quindicina di postazioni va benissimo.

16 Risposte

  • Non si capisce esattamente cosa stai chiedendo, o forse non lo capisco io

    Se volessi riformulare la domanda in modo piu' chiaro, te ne sarei grato

    Cosa intendi esattamente con "..grosso applicativo Access.." ?

    Se lo estrai coi vari strumenti vcs tipo il Jolly-Service o simili, quante form/report/moduli/query ti spara fuori?   10 Mbyte / 100 Mbyte (parlo dei file estratti in formato testo) ?

    "..Colgo anche l'occasione per un bilancio del mio metodo.."  se ci chiarisci di che si parla magari qualche complimento arriva (o qualche critica)

  • 21/05/2026 - amorosik ha scritto:

    Non si capisce esattamente cosa stai chiedendo, o forse non lo capisco io

    Se volessi riformulare la domanda in modo piu' chiaro, te ne sarei grato

    Cosa intendi esattamente con "..grosso applicativo Access.." ?

    Se lo estrai coi vari strumenti vcs tipo il Jolly-Service o simili, quante form/report/moduli/query ti spara fuori?   10 Mbyte / 100 Mbyte (parlo dei file estratti in formato testo) ?

    "..Colgo anche l'occasione per un bilancio del mio metodo.."  se ci chiarisci di che si parla magari qualche complimento arriva (o qualche critica)

    Avrei voluto scrivere sulla vecchia discussione, non potendo ho messo il link, lì è tutto spiegato. In ogni caso non chiedo ne voglio nulla, avverto solo di aver trovato finalmente la ragione per cui le form si rovinavano, magari qualcuno può evitare il problema, e avvertire chi avesse interesse alla soluzione delle tabelle temporanee che ho spiegato che si è dimostrata molto efficiente. Tutto qui.

  • 22/05/2026 - Catafirro ha scritto:

    Avrei voluto scrivere sulla vecchia discussione, non potendo ho messo il link, lì è tutto spiegato. In ogni caso non chiedo ne voglio nulla, avverto solo di aver trovato finalmente la ragione per cui le form si rovinavano, magari qualcuno può evitare il problema, e avvertire chi avesse interesse alla soluzione delle tabelle temporanee che ho spiegato che si è dimostrata molto efficiente. Tutto qui.

    Come ti è stato detto precedentemente, il tuo approccio è sbagliato. Anche distruggere il form alla terza chiusura non ha senso.

    Quando si decide di creare il form al bisogno e distruggerlo quando non serve più appena si rende necessario il form si ricrea. In ogni caso, tu distruggi un form con dipendenze da form chiamante. Da lì l'errore generico che racchiude più casistiche che lo generano. I form li puoi distruggere quando il form principale viene chiuso, tu distruggi il form che deve riportare informazioni necessarie al form principale, ma distruggendolo non sono più disponibili le informazioni che il form richiede e da lì nasce l'errore. Puoi distruggere il form anche al primo close ma se il form viene evocato dal principale è il principale che deve distruggerlo, dopo aver prelevato le info che servono. In poche parole, se form1 fa create forme è form 1 che deve fare form2 destroy (o qualunque sia il comando per distruggere il form).

    Form2 create
    Fai quello che deve fare form2
    Chiudi form2
    Dopo la chiusura torni a form1 e quindi
    Form2.destroy o free o altro in base al linguaggio

    Se form1 crea form2 per avere dei dati e quando esci da form2 lo distruggi, form 1 non ha più accesso alle informazioni perché form2 non esiste più insieme ai suoi controlli e sue variabili.

    Ergo, la soluzione che hai architettato è ancora precaria, in più devi gestire i casi di prima e seconda chiusura che non distruggono il form.

    P.s. se vuoi puoi sostituire la parola distruggere con sfasciare ma nel primo caso la soluzione sarebbe arrivata all'istante. Distruggere significa che ho eliminato il form, sfasciare significa che qualcosa non funziona più.

  • 22/05/2026 - Catafirro ha scritto:

    21/05/2026 - amorosik ha scritto:

    Non si capisce esattamente cosa stai chiedendo, o forse non lo capisco io

    Se volessi riformulare la domanda in modo piu' chiaro, te ne sarei grato

    Cosa intendi esattamente con "..grosso applicativo Access.." ?

    Se lo estrai coi vari strumenti vcs tipo il Jolly-Service o simili, quante form/report/moduli/query ti spara fuori?   10 Mbyte / 100 Mbyte (parlo dei file estratti in formato testo) ?

    "..Colgo anche l'occasione per un bilancio del mio metodo.."  se ci chiarisci di che si parla magari qualche complimento arriva (o qualche critica)

    Avrei voluto scrivere sulla vecchia discussione, non potendo ho messo il link, lì è tutto spiegato. In ogni caso non chiedo ne voglio nulla, avverto solo di aver trovato finalmente la ragione per cui le form si rovinavano, magari qualcuno può evitare il problema, e avvertire chi avesse interesse alla soluzione delle tabelle temporanee che ho spiegato che si è dimostrata molto efficiente. Tutto qui.

    Buon per te che hai trovato la soluzione delle form scassate

    Io ho interesse per la soluzione delle tabelle temporanee

    Mi sono riletto la discussione precedente perche' pensavo di essermi perso qualcosa, ti e' stato chiesto quale fosse l'utilita' di avere dei doppioni di dati (su tabelle locali) rispetto a lavorare coi dati sul db server, ma l'unica risposta che e' arrivata e'  "..non è che posso qui raccontarvi come è fatto il mio software..."

    Poi leggo 

    "..la mia soluzione per la multiconcorrenza non l'avete capita.." in cosa differirebbe la tua multiconcorrenza dalla 'concorrenza' classica?

    "..al momento di scrivere su server si controlla che qualcuno non abbia modificato dopo il nostro EDIT (tramite un campo DATETIME.." sei sicuro che funzioni? Come puoi sapere che un'altra postazione non abbia scritto DOPO che hai eseguito la lettura e PRIMA che tu abbia scritto? Comuqnue faresti anche presto a verificarlo, metti alcune postazioni a scrivere in loop continuo sul db e vedi se dopo qualche giorno di test qualcuna si incastra

    Allora come adesso non mi e' ben chiaro quale sia il motivo per cui stai usando metodi di questo tipo quando potresti usare le classiche transazioni disponibili su qualsiasi db server

    Per quanto riguarda le tabelle locali, supponendo ce ne fosse bisogno, potresti tenere i dati su recordset ado in memoria, al posto di scriverli fisicamente in locale, con vantaggi per le prestazioni, certo che mettere mano ad un codice che e' gia' in funzione non e' granche' igienico

    Ma se stai perorando la causa delle tabelle temporanee locali e ritieni che abbiano dei vantaggi rispetto ad altre soluzioni, beh allora parliamone, personalmente non credo abbiano vantaggi rispetto a lavorare coi dati sul db server, ma sono ben disponibile ad accettare scenari diversi da quelli che ho visto finora e magari imparare qualcosa di nuovo

    Certo che se ti trinceri dietro al "..mi vedo sottoposto a giudizio.." allora mi sa che qualsiasi ipotesi prospettabile possa essere inutile perdita di tempo

  • 15/05/2026 - Catafirro ha scritto:

    Molto tempo fa avevo chiesto aiuto per un problema di form che si corrompono dopo la chiusura senza apparente motivo in un grosso applicativo access. 

    https://www.iprogrammatori.it/forum-programmazione/access/form-che-si-sfasciano-t54170.html

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

    RICHIESTA PER L'AMMINISTRATORE

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

    Ma per quale motivo e' impossibile proseguire una discussione di oltre un anno fa?

    E' indubbio che la soluzione di una certa problematica possa arrivare, come in questo caso, anche parecchio tempo dopo, dover separare un flusso di domande-risposte in 3d separati rende sicuramente meno efficace l'utilizzo del forum

    Vorre far riconsiderare la scelta di bloccare i 3d su base temporale, e' una funzionalita' che a mio avviso peggiora la fruibilita' dei vari argoenti trattati sui vari forum

  • Ciao,

    purtroppo erano più gli utenti che postavano un loro problema in coda a una discussione vecchia e conclusa che quelli che trovavano la soluzione dopo un anno.

    La bilancia dei pro e i contro ha portato a implementare questa funzione automatica.

    In questi casi basta creare una nuova discussione e linkare la vecchia.

    Un saluto 

  • 23/05/2026 - sihsandrea ha scritto:

    22/05/2026 - Catafirro ha scritto:

    Avrei voluto scrivere sulla vecchia discussione, non potendo ho messo il link, lì è tutto spiegato. In ogni caso non chiedo ne voglio nulla, avverto solo di aver trovato finalmente la ragione per cui le form si rovinavano, magari qualcuno può evitare il problema, e avvertire chi avesse interesse alla soluzione delle tabelle temporanee che ho spiegato che si è dimostrata molto efficiente. Tutto qui.

    Come ti è stato detto precedentemente, il tuo approccio è sbagliato. Anche distruggere il form alla terza chiusura non ha senso.

    Quando si decide di creare il form al bisogno e distruggerlo quando non serve più appena si rende necessario il form si ricrea. In ogni caso, tu distruggi un form con dipendenze da form chiamante. Da lì l'errore generico che racchiude più casistiche che lo generano. I form li puoi distruggere quando il form principale viene chiuso, tu distruggi il form che deve riportare informazioni necessarie al form principale, ma distruggendolo non sono più disponibili le informazioni che il form richiede e da lì nasce l'errore. Puoi distruggere il form anche al primo close ma se il form viene evocato dal principale è il principale che deve distruggerlo, dopo aver prelevato le info che servono. In poche parole, se form1 fa create forme è form 1 che deve fare form2 destroy (o qualunque sia il comando per distruggere il form).

    Form2 create
    Fai quello che deve fare form2
    Chiudi form2
    Dopo la chiusura torni a form1 e quindi
    Form2.destroy o free o altro in base al linguaggio

    Se form1 crea form2 per avere dei dati e quando esci da form2 lo distruggi, form 1 non ha più accesso alle informazioni perché form2 non esiste più insieme ai suoi controlli e sue variabili.

    Ergo, la soluzione che hai architettato è ancora precaria, in più devi gestire i casi di prima e seconda chiusura che non distruggono il form.

    P.s. se vuoi puoi sostituire la parola distruggere con sfasciare ma nel primo caso la soluzione sarebbe arrivata all'istante. Distruggere significa che ho eliminato il form, sfasciare significa che qualcosa non funziona più.

    Sicuramente per colpa mia, ma non credo tu abbia capito. Non c'è alcuna dipendenza tra le mie form, si tratta delle form multiistanza che vengono aperte non con il classico  "DoCmd.OpenForm XXXXX", che consente di aprire una sola forma, ma con la sequenza

    Public MultiForm As New Collection
    ....
    ....
    
    Set frm = New Form_XXXXXX
    
    frm.Visible = True
    
    MultiForm.Add frm, frm.hwnd & ""
    

    La collection Multiform contiene gli oggetti delle form aperte, la form esiste fino a che esiste il suo riferimento nella collection. Quando la form viene chiusa, sarebbe buona norma eliminare l'oggetto dalla collection, che altrimenti rischia di gonfiarsi di oggetti non più validi. Il problema del mio codice era che eliminavo l'oggetto nell'evento FORM_CLOSE della stessa form, che così si trovava ad esserne privata ancora prima di uscire definitivamente dalla memoria. La cosa non mi aveva mai dato problemi nella vecchia versione dell'applicativo, che interfacciava un backend access, in questa che interfaccia un db MySql sporadicamente rovinava la form nel repository. Quando me ne sono accorto ho evitato di eliminare l'oggetto dalla collection a chiusura maschera, ma lo faccio fare a una routine che entra in gioco periodicamente, scorre la collection, e quando trova un oggetto non più valido (corrispondente a una maschera chiusa) lo rimuove. Così tutto funziona benissimo.

    La soluzione temporanea che avevo messo in piedi può comunque continuare ad avere un senso, poiché evita di usare la collection alla prima apertura (operazione che comunque costringe Access a lavorare di più), che viene eseguita con il classico DoCmd.OpenForm ed evita di aprire e chiudere le form di uso molto frequente, adottando la soluzione di renderle visibili/invisibili invece di aprile/chiuderle, con un percettibile miglioramento di performance per form molto pesanti.

    Riguardo la mia soluzione delle tabelle temporanee, che funziona benissimo sul campo, la riassumo brevemente

    1. Per ogni tabella MySql ho una sua immagine Access, che eventualmente può avere anche dei campi in più di comodo

    2. Quando disegno una form, la collego alla tabella Access, mai alla tabella MySql che non collego neppure

    3 Nell'evento Form_Open genero un clone temporaneo della tabella Access e lo collego alla form al posto di quella originale. Ho così una tabella temporanea nella quale posso caricare i record MYSql.

    4 Carico i dati nella tabella temporanea attraverso recordset ADO

    5 Le modiche ai dati vengono effettuate nella tabella temporanea

    6 A conferma delle modifiche i dati vengono aggiornati nel db MySql via recordset ADO.

    Eventuali campi in più nella tabella Access possono servire per visualizzare dati complessi non esistenti nel db MySql

    Sia ben chiaro che non sono qui per insegnare il mestiere a nessuno, magari esistono soluzioni migliori della mia, senz'altro, forse però qualcuno può anche avere interesse a un approccio così fatto.

  • 27/05/2026 - Catafirro ha scritto:

    Riguardo la mia soluzione delle tabelle temporanee, che funziona benissimo sul campo, la riassumo brevemente ....

    Come ti avevo già scritto nel post originale hai ricreato il funzionamento dei recordset disconnessi di ADO (fatto salvo l'aggiunta di campi non presenti nelle tabelle mysql) ... 

    Curiosità mia: come gestisci gli aggiornamenti e la concorrenza?

    Ipotizzo che il tuo punto n°6 sia gestito sfruttando un campo timestamp di ogni record e l'update sia fatto solo per quei records che hanno il timestamp locale diverso da quello del server mysql (ma se un altro utente ha modificato il record e tu non l'hai fatto localmente diventa un problema) oppure aggiorni solo quello modificati localmente per qui records che hanno il timestamp attuale diverso da timestamp.oldvalue (qui potresti trovarti nella situazione di tentare di aggiornare records già aggiornati da altri utenti, come te ne accorgi?)

    Come gestisci l'eventuale cancellazione da parte di altro utente di un record che tu hai modificato localmente?

  • 27/05/2026 - Catafirro ha scritto:

    Quando me ne sono accorto ho evitato di eliminare l'oggetto dalla collection a chiusura maschera, ma lo faccio fare a una routine che entra in gioco periodicamente

    ma scusa, se intercetti la chiusura del form?

    Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    
        Dim Risposta As VbMsgBoxResult
    
        If CloseMode = vbFormControlMenu Then
    
            Risposta = MsgBox( _
                "Vuoi chiudere definitivamente la finestra?" & vbCrLf & _
                "Premi NO per mantenerla in memoria.", _
                vbYesNoCancel + vbQuestion, _
                "Chiusura form")
    
            Select Case Risposta
    
                Case vbYes
                    ' chiusura reale
                    On Error Resume Next
                    MultiForm.Remove CStr(Me.hwnd)
    
                Case vbNo
                    ' mantiene viva la form
                    Cancel = True
                    Me.Hide
    
                Case vbCancel
                    ' annulla tutto
                    Cancel = True
    
            End Select
    
        End If
    
    End Sub

    fermo restando tutto il resto che ti è stato detto del modus operandi...

  • 27/05/2026 - sihsandrea ha scritto:

    27/05/2026 - Catafirro ha scritto:

    Quando me ne sono accorto ho evitato di eliminare l'oggetto dalla collection a chiusura maschera, ma lo faccio fare a una routine che entra in gioco periodicamente

    ma scusa, se intercetti la chiusura del form?

    Sarà che utilizzo una versione datata di Access (2013), ma 'UserForm_QueryClose' non lo vedo ... comunque lo scopo è quello di eliminare il riferimento del form dalla collection DOPO che il form è stato chiuso e quindi mi sembra che sia un poco difficile farlo dal form stesso (in quanto è ancora aperto).

    Ipotizzo che il form venga aperto (e inserito nella collection) tramite pulsante specifico quindi una soluzione potrebbe essere quella di avere un ulteriore pulsante specifico al di fuori del form, che effettua la chiusura del form (al quale è stata rimossa la possibilità di venir chiuso tramite la x) e la sua cancellazione dalla collection. Oppure permettere la chiusura normale dal form stesso e poi tramite evento gestito con timer andare a cancellarlo dalla collection (come fatto da Catafirro).

  • 27/05/2026 - max.riservo ha scritto:

    27/05/2026 - Catafirro ha scritto:

    Riguardo la mia soluzione delle tabelle temporanee, che funziona benissimo sul campo, la riassumo brevemente ....

    Come ti avevo già scritto nel post originale hai ricreato il funzionamento dei recordset disconnessi di ADO (fatto salvo l'aggiunta di campi non presenti nelle tabelle mysql) ... 

    Curiosità mia: come gestisci gli aggiornamenti e la concorrenza?

    Ipotizzo che il tuo punto n°6 sia gestito sfruttando un campo timestamp di ogni record e l'update sia fatto solo per quei records che hanno il timestamp locale diverso da quello del server mysql (ma se un altro utente ha modificato il record e tu non l'hai fatto localmente diventa un problema) oppure aggiorni solo quello modificati localmente per qui records che hanno il timestamp attuale diverso da timestamp.oldvalue (qui potresti trovarti nella situazione di tentare di aggiornare records già aggiornati da altri utenti, come te ne accorgi?)

    Come gestisci l'eventuale cancellazione da parte di altro utente di un record che tu hai modificato localmente?

    I record mysql hanno un campo datetime (chiamiamolo UpdDt) che indica data e ora di ultimo aggiornamento, e un campo che indica l'utente che ha fatto quell'aggiornamento (chiamiamolo UtNrUpd). Quando l'utente chiede l'edit del record che sta visualizzando, questo viene riletto e viene memorizzato in una variabile il suo campo UpdDt. Durante la fase di edit non c'è nessun blocco del record MySql, che viene lasciato libero. 

    Nel momento in cui l'utente fa SAVE dei dati modificati sulla tabella locale, viene letto ancora una volta il record. Se il campo UpdDt non è cambiato le modifiche vengono trasferite via ADO sulla tabella remota e vengono aggiornati sia il campo UpdDt sia UtNrUpd. Se il campo UpdDt è invece cambiato, si avverte l'utente che non è possibile salvare, poiché qualcuno nel frattempo ha modificato i dati. I dati di maschera non vengono subito resettati, il che consente di parcheggiare negli appunti eventuali stringhe complesse, come annotazioni, in ogni caso l'operazione di modifica deve essere ripetuta ripartendo dai nuovo dati.

    Per il tipo di dati che l'applicativo gestisce i conflitti sono molto rari, e gli utenti non se ne lamentano. Solo nel caso di una tabella cardine ho preferito implementare un sistema di avvertimento, non vincolante però: in quel caso e solo in quel caso quando si entra in edit viene inserito un semaforo, cosicché se un altro utente entra in edit sullo stesso record viene avvertito del conflitto, e può decidere quindi di non proseguire. La funzione non è comunque bloccante per evitare che si creino problemi con eventuali situazioni di precedenti crash.

    Il ragionamento vale anche nel caso speciale di cancellazione. Se alla richiesta di SAVE ci si accorge che il record è stato cancellato, si avverte e si esce dalla modifica posizionandosi su un record differente.

    Il vantaggio del sistema è che non si tengono bloccati mai i record durante le operazioni dell'utente, che naturalmente possono essere anche molto lunghe, magari comprendendo la pausa caffé. Le transazioni sono ridotte al minimo indispensabile, il che è sempre un bene, visto che comunque si tratta di una prestazione sempre molto delicata.

  • 27/05/2026 - Catafirro ha scritto:

    I record mysql hanno un campo datetime

    Hanno anche un motore myisam o innodb, dai una lettura alle.differze e poi controlla che motore stai usando tu.

    27/05/2026 - Catafirro ha scritto:

    Se il campo UpdDt è invece cambiato, si avverte l'utente che non è possibile salvare,

    Quindi mezz'ora a non fare niente? Se lo avvisi subito magari fa altro....

    Di solito chi gestisce db senza multiutenza usa bloccare il record per sbloccarlo quando ha finito. È la prima volta che vedo le cose fatte al contrario, magari domani si potranno costruire case per poi farci le fondamenta, un po' come avvenuto per la torre di Pisa.

    Era questo modo di gestione dati che ci ha portato fuori strada col titolo "form che si sfacsiano". 

    Io farei tesoro dei consigli che ti hanno dato nel 3d precedente poi ognuno fa come meglio crede.

Devi accedere o registrarti per scrivere nel forum
16 risposte