WinApi – GetLastError vs. Marshal.GetLastWin32Error

You must always use the Marshal.GetLastWin32Error. The main problem is the garbage collector. If it runs between the call of SetVolumeLabel and the call of GetLastError then you will receive the wrong value, because the GC has surely overwritten the last result.

Therefore you always need to specify the SetLastError=true in the DllImport-Attribute:

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

This ensures that the marhsallling stub calls immediately after the native function the “GetLastError” and stores it in the local thread.

And if you have specified this attribute then the call to Marshal.GetLastWin32Error will always have the correct value.

For more info see also “GetLastError and managed code” by Adam Nathan.

Also other function from .NET can change the windows “GetLastError”. Here is an example which produces different results:

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());
    }
  }
}

Also it seems that this is depended on the CLR which you are using! If you compile this with .NET2, it will produce “2 / 0”; if you switch to .NET 4, it will output “2 / 2″…

So it is depended on the CLR version, but you should not trust the native GetLastError function; always use the Marshal.GetLastWin32Error.

Leave a Comment