August 19th, 2015

If you are going to call Marshal.GetLastWin32Error, the function whose error you’re retrieving had better be the one called most recently

Even if you remember to set Set­Last­Error=true in your p/invoke signature, you still have to be careful with Marshal.Get­Last­Win32­Error 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 Open­Icon 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 Open­Icon. 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.Get­Last­Win32­Error immediately after calling Open­Icon. 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.
Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.