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