views:

2189

answers:

4

Hello,

How do I call WSAGetLastError() from WinAPI so I get the valid text error?

A: 

It doesn't make very much sense to call that function from managed code. It makes sense in unmanaged code because you know the exact last Win32 function that was called, so you know what function must have set the last error. In managed code, you don't know what functions have been called.

You could probably use P/Invoke to call the function; it just wouldn't do you any good. What are you trying to accomplish?

John Saunders
+2  A: 

WSAGetLastError is just a wrapper for the Win32 GetLastError function.

If you're doing things with P/Invoke, you can use the SetLastError parameter to the DllImport attribute. It tells .NET that the imported function will call SetLastError(), and that the value should be collected.

If the imported function fails, you can get at the last error with Marshal.GetLastWin32Error(). Alternatively, you can just throw new Win32Exception(), which uses this value automatically.

If you're not doing things with P/Invoke, you're out of luck: there's no guarantee that the last error value will be preserved long enough to make it back through multiple layers of .NET code. In fact, I'll link to Adam Nathan: never define a PInvoke signature for GetLastError.

Roger Lipscombe
+6  A: 
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern Int32 WSAGetLastError();

Also, on pinvoke.net it's said:

You should never PInvoke to GetLastError. Call Marshal.GetLastWin32Error instead!

System.Runtime.InteropServices.Marshal.GetLastWin32Error()

abatishchev
мерси! Comment.Length = 10;
John
+1  A: 

// This is how I saw on the web to put GetLastError into the C# exception mechanism // and how to get it back out again...

try
{
    // some p/invoke call that is going to fail with a windows error ...
    mHndActivatedDevice = MyNameSpace.Interop.Device.Device.ActivateDevice(
         "Drivers\\BuiltIn\\SomeDriverName", IntPtr.Zero, 0, IntPtr.Zero);
}
catch(System.ComponentModel.Win32Exception exc) // as suggested by John Saunders
{
    // you can get the last error like this:
    int lastError = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
    Console.WriteLine("error:" + lastError.ToString());

    // but it is also inside the exception, you can get it like this
    Console.WriteLine(exc.NativeErrorCode.ToString());

    Console.WriteLine(exc.ToString());
}

// where ActivateDevice is defined thus:

namespace MyNameSpace.Interop.Device { using System; using System.Runtime.InteropServices; using System.ComponentModel;

sealed class Device
{
    public static IntPtr ActivateDevice(String lpszDevKey, IntPtr lpRegEnts, UInt32 cRegEnts, IntPtr lpvParam)
    {
        // this is a little wrapper around the p/invoke to 
        // ActivateDeviceEx which checks the error
        // and throws an exception, thus C#-ifying the Win32 API call.
        IntPtr hndDriver = ActivateDeviceEx(lpszDevKey, lpRegEnts, cRegEnts, lpvParam);
        if (IntPtr.Zero == hndDriver)
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
        }
        return hndDriver;
    }

    [DllImport("Coredll.dll", EntryPoint = "ActivateDeviceEx", 
        SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern IntPtr ActivateDeviceEx(String lpszDevKey, 
        IntPtr lpRegEnts, UInt32 cRegEnts, IntPtr lpvParam);

    private Device() { }
}

}

David Fort
-1: The code should catch Win32Exception, not Exception. Otherwise, there's a chance of some other exception type being caught, then the case to Win32Exception failing.
John Saunders