假设我有一个带有此伪代码的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;
为什么GetLastError
函数在如上所示的DLL库中使用时返回0?有没有办法得到这种情况的最后一个错误代码?
答案 0 :(得分:10)
您对GetLastError
的调用会返回0
,因为在CreateFile
返回后调用了其他API,并且您的异常代码会执行。
GetLastError
线程局部变量返回的错误代码,在线程中运行的所有代码之间共享。因此,为了捕获错误代码,您需要在失败的函数返回后立即调用GetLastError
。
documentation解释如下:
调用线程执行的函数通过调用来设置该值 SetLastError 功能。您应该调用 GetLastError 函数 当函数的返回值指示这样的调用时立即 将返回有用的数据。那是因为有些函数会调用 SetLastError 成功时为零,消除错误代码 由最近失败的功能设置。
如果您使用TFileStream.Create
,则框架不会让您有机会在适当的时候致电GetLastError
。如果您确实想要获取该信息,则必须自己致电CreateFile
并使用THandleStream
代替TFileStream
。
有了THandleStream
的想法,你负责合成传递给THandleStream
的构造函数的文件句柄。这使您有机会在发生故障时捕获错误代码。
答案 1 :(得分:3)
在更高级别,此代码的真正问题在于它是混合模型。您正在尝试使用一个系统(VCL TStream系统)创建或打开文件,但您正在测试由其他系统(Win32 API)生成的错误。
您可以依赖Win32 GetLastError结果的唯一方法是自己调用Win32函数。为什么?因为这是确保在Win32函数调用和对GetLastError的调用之间没有其他Win32函数调用的唯一方法。每个Win32 API调用都有可能(重新)设置GetLastError。
即使VCL位于Win32之上,但在发生错误和异常到达处理程序之间时,还有很多其他Win32 API调用的机会。即使事情今天运转正常,VCL实施中的一些未来变化也很容易破坏当前形势下的快乐巧合。
避免这种“挂起时间”的最佳方法是,您需要的数据容易被覆盖,即将GetLastError值捕获尽可能接近故障点,并将其合并到VCL异常对象的属性中。这几乎可以消除你的异常处理程序和消除GetLastError全局状态的失败点之间的一些无辜的闯入者的风险。