为什么GetLastError在DLL库中调用时返回0?

时间:2013-03-15 17:17:29

标签: delphi delphi-2009

假设我有一个带有此伪代码的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?有没有办法得到这种情况的最后一个错误代码?

2 个答案:

答案 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全局状态的失败点之间的一些无辜的闯入者的风险。