Prevenire i crash alla chiusura dell`applicazione
Transcript
Prevenire i crash alla chiusura dell`applicazione
Prevenire i crash alla chiusura dell'applicazione Risoluzione del problema spinoso con alcune versioni di ComCtl32.DLL e di VB UserControls Alcune versioni della versione 6.0 di ComCtl32.DLL causano un arresto alla chiusura del programma quando sono caricati gli stili visivi di XP in un'applicazione. Ciò accade specialmente quando si usano i controlli utente di VB. Analizziamo due soluzioni al problema. Problema Il 40% circa dei miei progetti che supportano ComCtl32 per applicare gli stili visivi di XP ha cominciato improvvisamente ad arrestarsi quando un'applicazione veniva chiusa. Ci• • stato anche segnalato da molti utenti via mail che lamentavano dei problemi di arresto apparentemente inspiegabili. Dopo ore di ricerca, si è scoperto quanto segue: • ComCtl32.DLL era stata aggiornata rispetto alla mia installazione anteriore di XP. La nuova versione era 5.80.2800.1106, precedentemente la versione era 5.80.2600.0. • L'arresto si verificava soltanto: 1. Nei progetti che hanno incluso User Controls di Visual Basic. 2. Quando il progetto richiama la ComCtl32.DLL v6.0 (usando un file o una risorsa manifest). 3. Nel fare funzionare la versione eseguibile del progetto. • Aggiungendo finestre di messaggio all'applicazione si • appurato che l'arresto • avvenuto dopo che l'ultima linea del codice di VB era stata eseguita. Senza alcun riguardo alla complessit• del progetto, o a quanti form, l'arresto si • presentato soltanto durante l'arresto. GDI e le risorse dell'utente erano regolari. • Facendo funzionare un programma di debug per l'applicativo si • rilevato che l'arresto era causato da user32. DLL dopo qualche chiamata runtime VB. Dopo che molti tentativi per far funzionare applicativi differenti compilati in VB, si è provato con un'applicazione che conteneva soltanto un singolo UserControl senza una singola linea di codice in esso: e ancora si è verificato l'arresto. Quindi il problema non si poteva certo risolvere modificando il codice. VB non è stato progettato per supportare gli stili visivi di XP ed il service pack non era in grado di risolvere il problema. Due modi di risolvere il problema Ci sono due modi di risolvere questo problema. Il primo è una metodo "più corretto", mentre il secondo è frutto di invenzione che può essere applicato generalmente a qualunque tipo di programma che provoca un arresto alla chiusura. 1. La soluzione ufficiale Microsoft riconosce questo problema nell'articolo knowledge base KBID 309366 . Purtroppo, la causa del problema non è spiegata e la loro risoluzione suggerita non funziona. Tuttavia, pescando all'interno dell'articolo si rilevano delle informazioni che danno origine ad una soluzione. Tale soluzione deve usare la chiamata alla funzione LoadLibrary delle API della Shell32.DLL durante l'inizializzazione evento nel form che contiene il controllo: Private Declare Sub InitCommonControls Lib "comctl32.dll" () Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" ( ByVal lpLibFileName As String) As Long Private Declare Function FreeLibrary Lib "kernel32" ( ByVal hLibModule As Long) As Long Private m_hMod As Long Private Sub Form_Initialize() m_hMod = LoadLibrary("shell32.dll") InitCommonControls End Sub Private Sub Form_Unload(Cancel As Integer) FreeLibrary m_hMod End Sub Una volta fatto questo, gli arresti non si sono più verificati. Sembra che lo stesso codice possa essere usato direttamente all'interno propri UserControl, negli eventi _Initialize e _Terminate, avendo ovviamente accesso al codice sorgente del controllo. 2. Ignorare l'errore e procedere Ignorare l'errore non è la soluzione più elegante, ma può essere facile e questa tecnica pu• venire utile nella risoluzione degli altri problemi. In questo caso non stiamo usufruendo delle risorse, tutto sta terminando e chiudendosi nell'ordine corretto, ma sorge il problema a causa del tempo di esecuzione di VB, cosa che accade soltanto quando VB pulisce dopo che tutto il codice di esecuzione è terminato. Così, notando che l'arresto accade soltanto dopo che l'applicativo ha terminato il proprio lavoro, sappiamo che il processo sta chiudere tutte le istanze aperte. Poiché ogni applicazione funziona in un relativo proprio spazio di memoria, Windows sta cercando di riordinare la memoria stessa dove l'applicativo ha lasciato traccia. Quindi è questa traccia in memoria che continua ad interferire con le altre applicazioni, a meno che VB non abbia installato qualcosa nel sistema, cosa che è da considerare come altamente improbabile. Di conseguenza possiamo risolvere questo problema semplicemente ignorandolo . L'unico problema in questo modo che l'applicazione continua a inviare messaggi al sistema e danno origine alla nota finestra che invita a trasmettere le informazioni del Microsoft sul problema: Fortunatamente, Windows offre un modo per evitare questi avvisi, attraverso il trattamento delle eccezioni strutturato delle API. Mentre questa api permette di intercettare questi messaggi e mantenere il funzionamento della vostra applicazione, in questo caso non ci preoccupiamo a tale proposito, poiché tutto il nostro codice ha terminato di funzionare e desideriamo solo arrestare la visualizzazione di questo messaggio. Fate questo chiamando la funzione API SetErrorMode: Private Declare Function SetErrorMode Lib "kernel32" ( _ ByVal wMode As Long) As Long Private Const SEM_FAILCRITICALERRORS = &H1 Private Const SEM_NOGPFAULTERRORBOX = &H2 Private Const SEM_NOOPENFILEERRORBOX = &H8000& ... ' Previene tutte le finestre di dialogo e i message box di errore del sistema (UAE): SetErrorMode SEM_NOGPFAULTERRORBOX Ciò indirizza Windows a non visualizzare la finestra di messaggio di errore. Ora tutto quello che dobbiamo fare è richiamare questa funzione attraverso l'ultima linea del codice eseguito dall'applicazione. Notate che potreste richiamare la funzione con la prima linea di codice, ma in tal caso tutto il UAE realmente connesso con il codice non verrebbe gestito dall'applicazione e ciò comporterebbe una esecuzione in condizioni non ideali. Inoltre è stato aggiunto un certo codice in modo da non fare la chiamata all'IDE, poiché il problema si presenta soltanto negli eseguibili compilati: Private Declare Function SetErrorMode Lib "kernel32" ( ByVal wMode As Long) As Long Private Const SEM_NOGPFAULTERRORBOX = &H2& Private m_bInIDE As Boolean Public Sub UnloadApp() If Not InIDE() Then SetErrorMode SEM_NOGPFAULTERRORBOX End If End Sub Public Property Get InIDE() As Boolean Debug.Assert (IsInIDE()) InIDE = m_bInIDE End Property Private Function IsInIDE() As Boolean m_bInIDE = True IsInIDE = m_bInIDE End Function Determinare dove e quando richiamare questa funzione è cosa specifica per ogni applicazione, tuttavia funzionerà nella maggior parte dei casi aggiungendo la seguente linea di codice a ciascuna evento _Terminate dei vostri form: Private Sub Form_Terminate() If (Forms.Count = 0) Then UnloadApp End If End Sub Se questo non funziona nel vostro applicativo, dovete verificare in quale punto si verifica il problema, in modo da richiamare il codice in quel punto. Conclusione Applicando le nuove caratteristiche alle applicazioni più vecchie si possono riscontrare problemi inattesi, poiché non sono state progettate per il nuovo sistema operativo. Tuttavia, non è sempre pratico aggiornare il codice all'ultima versione ed a volte i lavori di "rappezzo" sono necessari. ComCtl32.DLL di Microsoft fornisce un gran numero di caratteristiche eccellenti ma le versioni aggiornate di questa DLL hanno causato problemi con applicazioni pe esistenti. Questo stato di cose sembra continuare, malgrado la disponibilità di nuovi service packs. Le soluzioni proposte al problema dei UAE (il famoso errore "Invia a Microsoft...") per le applicazioni di VB che usano gli stili visivi di XP descritti in questo articolo non sono ideali, ma sono molto facili da applicarsi e comunque sono funzionanti. Fonte: Mauro Rossi DrumiX Web