2013-03-15 53 views
6

假設我有這個僞代碼DLL庫:GetLastError爲什麼在DLL庫中調用時返回0?

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; 

爲什麼GetLastError函數返回0,當它在一個DLL庫中使用如上所示?有沒有辦法得到這種情況下的最後一個錯誤代碼?

+1

你會期望它是什麼? GetLastError返回Windows API函數生成的最後一個錯誤,並且只有一個插槽,因此Delphi異常處理系統可能對Windows API執行的操作可能會重置或更改該值。通常,與LastError的值相關的自定義異常將值包裝在自定義屬性中。 – 2013-03-15 17:25:08

+2

您必須在API調用後立即調用它。你沒有這樣做。嘗試在失敗的CreateFile後立即執行此操作。底線是你的代碼無效。 – 2013-03-15 17:25:27

回答

10

您致電GetLastError返回0,因爲還有其他API在CreateFile返回後調用,並執行您的異常代碼。

GetLastError線程本地變量返回的錯誤代碼,並在您的線程中運行的所有代碼之間共享。因此,爲了捕獲錯誤代碼,您需要在失敗的函數返回後立即致電GetLastError。通過調用 SetLastError功能通過設置這個值調用線程執行

功能:

documentation解釋它是這樣的。當函數的返回值指示這樣的調用 將返回有用的數據時,應立即調用GetLastError函數 。這是因爲一些函數在成功時調用 SetLastError,並清除由最近失敗的函數設置的錯誤代碼 。

如果您使用的是TFileStream.Create,那麼該框架不會讓您有機會在適當的時刻致電GetLastError。如果您真的想獲得這些信息,您必須自己撥打CreateFile,並使用THandleStream而不是TFileStream

有這樣的想法,即THandleStream您負責合成文件句柄,並將其傳遞給THandleStream的構造函數。這使您有機會在發生故障時捕獲錯誤代碼。

+0

所有這些聽起來都很合理,但我仍然想知道什麼會重置錯誤。它必須是調用者和庫之間的東西,因爲如果你在VCL應用程序中運行該代碼,它會返回適當的錯誤代碼(儘管你自己應該立即在'CreateFile'後立即調用它)。 – TLama 2013-03-15 17:40:35

+1

我並不是很願意分析這一點。很明顯你是如何使用GetLastError的。 – 2013-03-15 17:48:09

+0

我的不好,我應該用不同的方式說這個問題。沒關係。不管怎麼說,還是要謝謝你! – TLama 2013-03-15 17:55:42

3

在更高層次上,這個代碼的真正問題在於它是混合模型。您正嘗試使用一個系統(VCL TStream系統)創建或打開一個文件,但是您正在測試由不同系統(Win32 API)產生的錯誤。

您可以依靠Win32 GetLastError結果的唯一方法是如果您自己調用Win32函數。爲什麼?因爲這是確保在Win32函數調用和對GetLastError的調用之間沒有其他對Win32函數的調用的唯一方法。每個Win32 API調用都有可能(重新)設置GetLastError。

儘管VCL位於Win32之上,但在發生錯誤和異常到達處理程序之間還有很多其他Win32 API調用的機會。即使事情今天運行良好,VCL實施中的一些未來變化可能很容易破壞當前情況的快樂巧合。

要避免這種「掛起時間」,您需要的數據容易被覆蓋的最佳方法是將GetLastError值捕獲爲儘可能接近故障點並納入VCL異常對象的屬性中。這幾乎可以消除你的異常處理程序和失敗點之間一些無辜interloper的風險,從而消除GetLastError全局狀態。

相關問題