static void Main(string[] args) {
if (!NativeMethods.DeleteFileW(@"X:\NonExisting.txt")) {
throw new Win32Exception(Marshal.GetLastWin32Error().ToString());
}
}
private static class NativeMethods {
[DllImportAttribute("Kernel32.dll", EntryPoint = "DeleteFileW", CharSet = CharSet.Unicode)]
[return: MarshalAsAttribute(UnmanagedType.Bool)]
public static extern bool DeleteFileW([InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpFileName);
}
This code looks quite straightforward. Function should delete file and in case of failure exception will be raised. In this particular case one would be expect either 2 (ERROR_FILE_NOT_FOUND) or 3 (ERROR_PATH_NOT_FOUND) as raised exception’s text.
However this example will most probably return 0. You can already guess from highlight that problem is in DllImport line. To make function behave properly, one parameter needs to be added:
[DllImportAttribute("Kernel32.dll", EntryPoint = "DeleteFileW", CharSet = CharSet.Unicode, SetLastError = true)]
When SetLastError is set to true, .NET will make all necessary plumbing in order to catch and store error code. This code will be available to you in GetLastWin32Error() function until some other P/Interop call overwrites it.
Although I knew all of that, I fell as a pray to this annoying bug. Reason lies in great tool - PInvoke Interop Assistant. It will generate all necessary P/Interop signatures and it is just too easy to rely on it’s judgement. At least until you find out that all those API signatures are generated without SetLastError parameter set.
If this little issue goes unnoticed, you have quite a big bug on your hands. Worse still, since GetLastWin32Error() will almost always return 0, bug will probably go unnoticed and it will manifest itself somewhere far away from function. Catching that one can be tricky.