Even if you remember to set SetLastError=true
in your p/invoke signature, you still have to be careful with Marshal.GetLastWin32Error
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.GetLastWin32Error
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.
0 comments