Even if you
remember to
set SetLastError=true
in your p/invoke signature,
you still have to be careful with
Marshal.
because there is only one last-error code,
and it gets overwritten each time.
So let's try this program:
using System; using System.Runtime.InteropServices; class Program { [DllImport("user32.dll", SetLastError=true)] public static extern bool OpenIcon(IntPtr hwnd); public static void Main() { // Intentionally pass an invalid parameter. var result = OpenIcon(IntPtr.Zero); Console.WriteLine("result: {0}", result); Console.WriteLine("last error = {0}", Marshal.GetLastWin32Error()); } }
The expectation is that the call to
OpenIcon
will fail,
and the error code will be some form of invalid
parameter.
But when you run the program, it prints this:
result: False last error = 0
Zero?
Zero means "No error".
But the function failed.
Where's our error code?
We printed the result immediately after
calling OpenIcon
.
We didn't call any other p/invoke functions.
The last-error code should still be there.
Oh wait, printing the result to the screen involves a function call.
That function call might itself do a p/invoke!
We have to call
Marshal.
immediately after calling
OpenIcon
.
Nothing else can sneak in between.
using System; using System.Runtime.InteropServices; class Program { [DllImport("user32.dll", SetLastError=true)] public static extern bool OpenIcon(IntPtr hwnd); public static void Main() { // Intentionally pass an invalid parameter. var result = OpenIcon(IntPtr.Zero); var lastError = Marshal.GetLastWin32Error(); Console.WriteLine("result: {0}", result); Console.WriteLine("last error = {0}", lstError); } }
Okay, now the program reports the error code as 1400: "Invalid window handle."
This one was pretty straightforward, because the function call that modified the last-error code was right there in front of us. But there are other ways that code can run which are more subtle.
- If you retrieve a property, the property retrieval may involve a p/invoke.
- If you access a class that has a static constructor, the static constructor will secretly run if this is the first time the class is used.