2013-03-15 11 views
6

Assumere Sto avendo una libreria DLL con questo pseudo-codice:Perché GetLastError restituisce 0 quando viene chiamato in una libreria DLL?

var 
    LastError: DWORD; 

procedure DoSomethingWrong; stdcall; 
var 
    FileStream: TFileStream; 
begin 
    try 
    FileStream := TFileStream.Create('?', fmCreate); 
    except 
    on EFCreateError do 
     LastError := GetLastError; // <- why does GetLastError return 0 here ? 
    end; 
end; 

Perché GetLastError funzione di ritorno 0 quando è utilizzato in una libreria DLL come indicato sopra? C'è un modo per ottenere l'ultimo codice di errore per questo caso?

+1

Cosa ti aspetti che sia? GetLastError restituisce l'ultimo errore generato da una funzione API di Windows e c'è solo uno slot, quindi qualsiasi cosa che il sistema di gestione delle eccezioni di Delphi potrebbe fare con l'API di Windows potrebbe reimpostare o modificare quel valore. Normalmente, le eccezioni personalizzate per le quali il valore di LastError è rilevante racchiudono il valore in una proprietà personalizzata. –

+2

Devi chiamarlo immediatamente dopo una chiamata API. Non lo stai facendo. Prova a farlo subito dopo un fallito CreateFile. La linea di fondo è che il tuo codice non è valido. –

risposta

10

La chiamata a GetLastError restituisce 0 perché ci sono altre API chiamate dopo i ritorni CreateFile e il codice di eccezione viene eseguito.

Il codice di errore restituito da GetLastError una variabile locale del thread e viene condiviso tra tutto il codice eseguito nel thread. Pertanto, per acquisire il codice di errore, è necessario chiamare GetLastError immediatamente dopo il ripristino della funzione non riuscita.

Il documentation spiega in questo modo:

funzioni eseguite dal thread chiamante impostare questo valore chiamando la funzione SetLastError. Dovresti chiamare la funzione GetLastError immediatamente quando il valore di ritorno di una funzione indica che tale chiamata restituirà dati utili. Questo perché alcune funzioni chiamano SetLastError con uno zero quando hanno successo, cancellando il codice di errore impostato dalla funzione fallita più recente.

Se si utilizza TFileStream.Create allora il quadro non ti dà l'opportunità di chiamare GetLastError al momento adatto. Se si desidera ottenere tali informazioni, è necessario chiamare personalmente lo CreateFile e utilizzare THandleStream anziché TFileStream.

L'idea è che con THandleStream si è responsabili per la sintesi dell'handle di file che si passa al costruttore di THandleStream. Questo ti dà l'opportunità di catturare il codice di errore in caso di errore.

+0

Tutto ciò sembra ragionevole, ma mi chiedo ancora, cosa resetta l'errore. Deve essere qualcosa tra il chiamante e la libreria poiché se si esegue quel codice in un'applicazione VCL, viene restituito il codice di errore corretto (anche se naturalmente è giusto che io debba chiamarlo immediatamente dopo "CreateFile" da solo). – TLama

+1

Non sono molto motivato ad analizzarlo. È chiaro come si intende utilizzare GetLastError. –

+0

Il mio male, dovrei esprimere questa domanda in modo diverso. Non importa. Grazie comunque! – TLama

3

A un livello superiore, il vero problema con questo codice è che sta mescolando i modelli. Stai tentando di creare o aprire un file con un sistema (sistema VCL TStream) ma stai testando gli errori prodotti da un sistema diverso (API Win32).

L'unico modo per fare affidamento sul risultato GetLastError Win32 è se si chiamano da soli le funzioni Win32. Perché? Perché questo è l'unico modo per garantire che non ci siano altre chiamate a funzioni Win32 tra la chiamata di funzione Win32 e la chiamata a GetLastError. Ogni chiamata API Win32 ha il potenziale per (re) impostare GetLastError.

Anche se VCL si trova in cima a Win32, è possibile che si verifichi un'altra chiamata API Win32 tra quando si verifica l'errore e quando l'eccezione raggiunge il gestore. Anche se oggi le cose andassero bene, alcuni cambiamenti futuri nell'implementazione VCL potrebbero facilmente interrompere la felice coincidenza che è la situazione attuale.

Il modo migliore per evitare questo "tempo di attesa" in cui i dati necessari sono vulnerabili a essere sovrascritti consiste nell'ottenere il valore GetLastError catturato il più vicino possibile al punto di errore e incorporato in una proprietà dell'oggetto di eccezione VCL .Ciò non farebbe altro che eliminare il rischio di alcuni interlocutori innocenti tra il gestore delle eccezioni e il punto di errore che cancella lo stato globale GetLastError.