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
.
[DllImport(SetLastError = true)]
and Marshal.GetLastWin32Error()
Marshal.GetLastWin32Error()
immediately after a failing Win32
call and on the same thread.As i read it, the official explanation why you need Marshal.GetLastWin32Error
can be found here:
The common language runtime can make internal calls to APIs that overwrite the GetLastError maintained by the operating system.
To say it in other words:
Between your Win32 call which sets the error, the CLR may "insert" other Win32 calls which could overwrite the error.
Specifying [DllImport(SetLastError = true)]
makes sure that the CLR retrieves the error code before the CLR executes any unexpected Win32 calls.
To access that variable we need to use Marshal.GetLastWin32Error
.
Now what @Bitterblue found is that these "inserted calls" don't happen often - he couldn't find any. But that's not really surpising. Why? Because it's extremely difficult to "black box test" whether GetLastError
works reliably:
There's is one specific component - the Garbage collector (GC) - which is known to interrupt a .net thread if there's memory pressure and do some processing on that thread (see What happens during a garbage collection). Now if the GC were to execute a failing Win32 call, this would break your call to GetLastError
.
To sum it up, you have a plethora of unknown factors which can influence the reliability of GetLastError
. You'll most likely not find an unreliability problem when developing/testing, but it might blow up in production at any time. So do use [DllImport(SetLastError = true)]
and Marshal.GetLastWin32Error()
and improve your sleep quality ;-)
in [DllImport("kernel32.dll", SetLastError = true)] does the SetLastError attribute make the Framework store the error code for the use of Marshal.GetLastWin32Error() ?
Yes, as is documented in DllImportAttribute.SetLastError Field
is there an example where plain GetLastError fails to give the correct result ?
As documented in Marshal.GetLastWin32Error Method, if the framework itself (e.g. the garbage collector) calls any native method that sets an error value between your calls to the native method and GetLastError
you would get the error value of the framework's call instead of your call.
do I really HAVE to use Marshal.GetLastWin32Error() ?
Since you can't ensure that the framework will never call a native method between your call and the call to GetLastError
, yes. Also, why not?
is this "problem" Framework version related ?
It could definitely be (e.g. changes in the garbage collector), but it doesn't have to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With