2012-09-24 13 views
6

Questa è un'applicazione Delphi, ma suppongo che sia una domanda generale di programmazione di Windows.Non capisco l'errore di overflow dello stack con ripetuto DispatchMessageW nello stack di chiamate

ho lasciato la mia applicazione in esecuzione (in Delphi IDE) durante il fine settimana e siamo appena tornati per trovare un overflow dello stack.

Lo stack inizia così ...

:75c4417e kernel32.GetDriveTypeW + 0x23 
:75c452ae kernel32.IsProcessorFeaturePresent + 0xa9 
:75c45272 kernel32.IsProcessorFeaturePresent + 0x6d 
:75c45248 kernel32.IsProcessorFeaturePresent + 0x43 
:7678410b KERNELBASE.LoadStringBaseExW + 0xc7 
:76678ed2 USER32.LoadStringW + 0x19 
:0040c4ae LoadResString + $4A 
uADStanDef.TADDefinition.Create(nil) 
uADStanDef.TADDefinition.CreateTemporary 
uADStanDef.TADConnectionDefTemporaryFactory.CreateObject 
uADStanFactory.TADManager.CreateInterface((1050358107, 62550, 16757, (168, 100, 178, 87, 60, 74, 32, 21)),(no value),True) 
uADStanFactory.ADCreateInterface((1050358107, 62550, 16757, (168, 100, 178, 87, 60, 74, 32, 21)),(no value),True) 
uADCompClient.TADCustomConnection.Create($2DB7EB0) 
fMainForm.TMainForm.ServerAliveTimerTimer($2E8DE38) <========== my code 
:004f1546 Winapi + $4F1546 
:00461316 Winapi + $461316 
:766762fa ; C:\Windows\syswow64\USER32.dll 
:76676d3a USER32.GetThreadDesktop + 0xd7 
:766777c4 ; C:\Windows\syswow64\USER32.dll 
:7667788a USER32.DispatchMessageW + 0xf 

Quindi, un timer è in scadenza, sto creando un nuovo oggetto (di un componente AnyDac) ei overflow dello stack. Il codice libera definitivamente l'oggetto. L'ho aggiunto qui sotto per coloro che vogliono controllare, ma non penso che questa sia la mia domanda.

Lo stack poi continua

:7669cdfd ; C:\Windows\syswow64\USER32.dll 
:7669cf5c ; C:\Windows\syswow64\USER32.dll 
:766cf73c ; C:\Windows\syswow64\USER32.dll 
:766cfa18 ; C:\Windows\syswow64\USER32.dll 
:766cfb1f USER32.MessageBoxTimeoutW + 0x52 
:766cfd15 USER32.MessageBoxExW + 0x1b 
:766cfd57 USER32.MessageBoxW + 0x18 
:00549986 Vcl + $549986 
:00549aa2 Vcl + $549AA2 
:00549873 Vcl + $549873 
:00461316 Winapi + $461316 
:766762fa ; C:\Windows\syswow64\USER32.dll 
:76676d3a USER32.GetThreadDesktop + 0xd7 
:766777c4 ; C:\Windows\syswow64\USER32.dll 
:7667788a USER32.DispatchMessageW + 0xf 

Con quel blocco ripetuto per tre thoussand linea (!) E non ho idea di che cosa è o che cosa sta facendo. E poi finisce

StoreRoom.StoreRoom 
:75c4339a kernel32.BaseThreadInitThunk + 0x12 
:77eb9ef2 ntdll.RtlInitializeExceptionChain + 0x63 
:77eb9ec5 ntdll.RtlInitializeExceptionChain + 0x36 

faccio alcuna tunderstand tutto questo pila ripetuto - chiunque può consigliare?

(E per l'astutute di voi che notare che la mia gestione delle eccezioni sta mostrando una finestra di dialogo, che è un TForm che si chiude quando l'utente fa clic su OK)

Il mio codice:

procedure TMainForm.ServerAliveTimerTimer(Sender: TObject); 
begin 
    try 
     ADConnection := TADConnection.Create(Self); <======= stack overflow here 
     ADConnection.DriverName := 'mysql'; 
     ADConnection.Params.Add('Server=' + MAIN_STOREROOM_IP_ADDRESS); 
     // other params, such as password, removed for posting 
     ADConnection.Connected := True; 

    except 
     on E : Exception do 
     begin 
     ADConnection.Free(); 
     theDialogForm := TDialogFormForm.Create(Nil); 
     theDialogForm.ShowTheForm('Database problem'+#13+#10+''+#13+#10+ 
            E.ClassName+#13+#10+E.Message);  
     StopTheApplication(); <===== just calls ExitProcess(0); 
     Exit;      as I had problems with Halt elsewhere in the code 
     end; 
    end; 

    if isMainStoreRoom then 
    begin 
     CheckIfStoreRoomIsAlive(SECONDARY_STOREROOM_IP_ADDRESS); 
    end 
    else 
    begin 
     CheckIfStoreRoomIsAlive(MAIN_STOREROOM_IP_ADDRESS); 
    end; 

    try // Now, update our own timestamp 
     timestamp := GetCurrentUnixTimeStamp(); 
     ADConnection.ExecSQL('UPDATE server_status SET alive_timestamp="' + IntToStr(timestamp) + '" WHERE ip_address="' + ipAddress + '"'); 

    except 
     on E : Exception do 
     begin 
     ADConnection.Free(); 
     Exit; 
     end; 
    end; 

    ADConnection.Free(); 
end;  // ServerAliveTimerTimer() 
+4

+1 per una domanda di overflow dello stack in caso di overflow dello stack. – lkessler

+2

Perché è necessario creare una classe di connessione ogni volta invece di utilizzare ADConnection.Connected True/False? Le impostazioni della connessione al database cambiano in fase di esecuzione? – pani

+0

La domanda era intitolata "Non capisco StackOverflow", ma purtroppo qualcuno lo ha modificato (+1 a tutti i likers) – Mawg

risposta

15

Il tuo stack l'overflow è dovuto al fatto che MessageBox() viene richiamato ripetutamente in risposta a un messaggio della finestra ricorrente. Internamente, MessageBox() esegue il proprio ciclo di messaggi, che è ovviamente l'elaborazione e la distribuzione dello stesso messaggio più e più volte. Ciò potrebbe indicare un timer che si è smarrito. Suggerisco caldamente di disabilitare il timer quando si immette per la prima volta il gestore di eventi OnTimer e quindi riattivare il timer prima di uscire.

Su una nota a parte, StopTheApplication() NON deve chiamare ExitProcess() (o anche Halt()) direttamente. Utilizzare invece Application.Terminate().

+0

+1 Grazie, @Remy. Ho omesso di menzionare che stavo correndo nell'IDE. Potrebbero essere le chiamate a MessageBox() da quello? Non lo chiamo affatto. Io chiamo MessageDlg(), ma non in qualsiasi gestore di timer - in quelli che creo e mostro un modulo di errore personalizzato. Ho solo tre timer e il codice del gestore per gli altri due è più breve di quello che ho postato. Prendo il tuo punto su disabilitare/abilitare i timer, ma il più breve (quello mostrato) è di 8 secondi. Alcuni accessi d/b non possono richiedere 8 secondi. – Mawg

+0

Attendi ... Non chiamo MessageBox(), ma forse il codice AnyDac lo fa, se c'era un errore nel database ... ma, anche allora, perché sarebbe in loop? AnyDac è ben testato ormai.Darò un'altra corsa con il timer disabilita/abilita il codice e abbrevierà le lunghezze del timer per provare ad accelerare il problema se si ripresenta – Mawg

+2

'MessageBox()' potrebbe essere chiamato da 'Application.MessageBox()' dal momento che viene chiamato da un pezzo di codice VCL nello stack di chiamate. 'Application.MessageBox()' viene talvolta chiamato dai gestori di eccezioni, ad esempio. Difficile da dire dato che lo stack delle chiamate non visualizza i nomi delle funzioni del VCL. Stai compilando per Release invece di Debug? In ogni caso, qualunque cosa stia chiamando 'MessageBox()' ovviamente non è scritto per essere rientranti e rimane bloccato in un ciclo ricorsivo, spingendo sempre più dati nello stack con ogni chiamata annidata a 'MessageBox()' fino a quando lo stack trabocca . –

Problemi correlati