c# - WinApi - GetLastError 与 Marshal.GetLastWin32Error

标签 c# winapi marshalling unmanaged managed

我测试了很多。但我没有发现这两个的缺点!
但是请参阅已接受的答案。


我读了here在托管代码中调用 GetLastError 是不安全的,因为框架可能会在内部“覆盖”最后一个错误。 GetLastError 我从来没有遇到过任何明显的问题,而且在我看来,.NET Framework 足够聪明,不会覆盖它。因此,我有几个关于该主题的问题:

  • [DllImport("kernel32.dll", SetLastError = true)]SetLastError 属性是否使框架存储使用 的错误代码编码(marshal).GetLastWin32Error() ?
  • 是否有普通 GetLastError 无法给出正确结果的示例?
  • 真的需要使用 Marshal.GetLastWin32Error() 吗?
  • 这个“问题”与框架版本有关吗?

public class ForceFailure
{
    [DllImport("kernel32.dll")]
    static extern uint GetLastError();
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);

    public static void Main()
    {
        if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
            System.Console.WriteLine("It worked???");
        else
        {
            // the first last error check is fine here:
            System.Console.WriteLine(GetLastError());
            System.Console.WriteLine(Marshal.GetLastWin32Error());
        }
    }
}


产生错误:

if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
    Console.WriteLine("It worked???");
else
{
    // bad programming but ok GetlLastError is overwritten:
    Console.WriteLine(Marshal.GetLastWin32Error());
    try
    {
        using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) { }
    }
    catch { }
    Console.WriteLine(GetLastError());
}

if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
    Console.WriteLine("It worked???");
else
{
    // bad programming and Marshal.GetLastWin32Error() is overwritten as well:
    Console.WriteLine(GetLastError());
    try
    {
        using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) { }
    }
    catch { }
    Console.WriteLine(Marshal.GetLastWin32Error());
}

// turn off concurrent GC
GC.Collect(); // doesn't effect any of the candidates

Console.WriteLine(" -> " + GetLastError());
Console.WriteLine(" -> " + GetLastError());
Console.WriteLine(Marshal.GetLastWin32Error());
Console.WriteLine(Marshal.GetLastWin32Error());
// when you exchange them -> same behaviour just turned around

我看不出有什么区别!两者的行为相同,除了 Marshal.GetLastWin32Error 也存储 App->CLR->WinApi 调用的结果,而 GetLastError 仅存储 App->WinApi 调用的结果。


Garbage Collection 似乎没有调用任何覆盖最后一个错误代码的 WinApi 函数

  • GetLastError 是线程安全的。 SetLastError 为调用它的每个线程存储一个错误代码。
  • GC 什么时候开始在我的线程中运行?

最佳答案

您必须始终使用 Marshal.GetLastWin32Error .主要问题是垃圾收集器。如果它在调用 SetVolumeLabel 和调用 GetLastError 之间运行,那么您将收到错误的值,因为 GC 肯定已经覆盖了最后的结果。

因此您始终需要指定 SetLastError=trueDllImport-属性中:

[DllImport("kernel32.dll", SetLastError=true)]
static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);

这确保编码 stub 在 native 函数“GetLastError”之后立即调用并将其存储在本地线程中。

如果您指定了此属性,则对 Marshal.GetLastWin32Error 的调用将始终具有正确的值。

有关详细信息,另请参阅 "GetLastError and managed code"亚当·内森。

.NET 中的其他函数也可以更改窗口“GetLastError”。这是一个产生不同结果的示例:

using System.IO;
using System.Runtime.InteropServices;

public class ForceFailure
{
  [DllImport("kernel32.dll")]
  public static extern uint GetLastError();

  [DllImport("kernel32.dll", SetLastError = true)]
  private static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);

  public static void Main()
  {
    if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
      System.Console.WriteLine("It worked???");
    else
    {
      System.Console.WriteLine(Marshal.GetLastWin32Error());
      try
      {
        using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) {}
      }
      catch
      {
      }
      System.Console.WriteLine(GetLastError());
    }
  }
}

而且这似乎取决于您使用的 CLR!如果你用 .NET2 编译它,它会产生“2/0”;如果切换到 .NET 4,它将输出“2/2”...

所以它取决于 CLR 版本,但你不应该相信原生的 GetLastError 函数;始终使用 Marshal.GetLastWin32Error

关于c# - WinApi - GetLastError 与 Marshal.GetLastWin32Error,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17918266/

相关文章:

.net - 什么 .NET UnmanagedType 是 Unicode (UTF-16)?

c# - 将 List 对象的值转换为字符串

winapi - 最小化所有打开的窗口?

delphi - 为什么 Alt 键没有触发我的低级键盘 Hook ?

c++ - TerminateThread() with CloseHandle() on thread which uses only stack plain variables (without alloc) 泄漏内存?

java - Jaxb 可以编码没有根元素的子元素吗?

c# - 运算符(operator) ?。和扩展方法

C# - Entity Framework - 了解一些基础知识

c# - Adobe PDF 阅读器控件 axAcroPDF 的文档

java - 解码 XML 文件中的一对一关系