Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not able to get PInvoke error message on Linux and MacOs #20660

Closed
DevMk opened this issue Mar 16, 2017 · 9 comments
Closed

Not able to get PInvoke error message on Linux and MacOs #20660

DevMk opened this issue Mar 16, 2017 · 9 comments
Labels
area-System.Runtime.InteropServices question Answer questions and provide assistance, not an issue with source code or documentation.
Milestone

Comments

@DevMk
Copy link

DevMk commented Mar 16, 2017

Hello,

I see that Marshal.GetLastWin32Error() returns proper errno, however Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()) always throws "The operation completed successfully" instead corresponding error.

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication {
 public class Program {
  [DllImport("libSystem.dylib", EntryPoint = "fopen", SetLastError = true)] // libc.so.6 for linux
  extern static private IntPtr FOpen(string path, string mode); // fopen was used for example. Same flow for other calls


  public static void Main(string[] args) {
   var f = FOpen("some-not-existing-file", "r");

   if (f == IntPtr.Zero) {
    Console.WriteLine("Error code is {0}", Marshal.GetLastWin32Error());

    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
   }
  }
 }
}

Actual result

Admins-Mac:console admin$ dotnet --version
1.0.1

Admins-Mac:console admin$ dotnet run
Error code is 2  <--- File not found error code

Unhandled Exception: System.Runtime.InteropServices.COMException: The operation completed successfully.
 (Exception from HRESULT: 0x80070000)
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
   at ConsoleApplication.Program.Main(String[] args) in /Users/admin/console/Program.cs:line 24

Expected result:

Exception with "No such file or directory" message

@mellinoe
Copy link
Contributor

I believe this is because you are calling GetLastWin32Error twice, essentially (once directly and once through the GetHRForLastWin32Error method). The first call is giving you the correct error code, but the second call returns "success" because the first call wiped the error code out. @stephentoub Is that accurate?

@DevMk
Copy link
Author

DevMk commented Mar 17, 2017

Yes, definitely. Thank you.

Is there a way to get both GetLastWin32Error and GetHRForLastWin32Error? Or get HR based on error.

One for special error handling, second one for exception message.

@danmoseley
Copy link
Member

danmoseley commented Mar 17, 2017

@DevMk you could inline this.

        public static int GetHRForLastWin32Error()
        {
            int dwLastError = GetLastWin32Error();
            if ((dwLastError & 0x80000000) == 0x80000000)
                return dwLastError;
            else
                return (dwLastError & 0x0000FFFF) | unchecked((int)0x80070000);
        }

@danmoseley
Copy link
Member

I think this is resolved -- please reopen if not.

@stephentoub
Copy link
Member

I believe this is because you are calling GetLastWin32Error twice, essentially (once directly and once through the GetHRForLastWin32Error method). The first call is giving you the correct error code, but the second call returns "success" because the first call wiped the error code out. @stephentoub Is that accurate?

Hmm... this appears to be a difference between desktop and core. This program:

using System;
using System.Runtime.InteropServices;

class Program
{
    static void Main()
    {
        CopyFile("doesntexist", "blah", true);
        Console.WriteLine(Marshal.GetLastWin32Error());
        Console.WriteLine(Marshal.GetLastWin32Error());
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);
}

on desktop prints:

2
2

but on core prints:

2
0

@yizhang82, @jkotas, is this difference by design?

@stephentoub stephentoub reopened this Mar 18, 2017
@stephentoub
Copy link
Member

stephentoub commented Mar 18, 2017

Ah, nevermind, I realized my example was flawed. This code:

using System;
using System.Runtime.InteropServices;

class Program
{
    static void Main()
    {
        CopyFile("doesntexist", "blah", true);
        int a = Marshal.GetLastWin32Error();
        int b = Marshal.GetLastWin32Error();
        Console.WriteLine(a);
        Console.WriteLine(b);
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);
}

prints out:

2
2

on core. The issue in my first example (and in the original example in this issue) is that Console.WriteLine is using a P/Invoke with SetLastError, and the known, by-design difference in core is that SetLastError zeros out the error code when making a P/Invoke (dotnet/coreclr#614).

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 2.0.0 milestone Jan 31, 2020
lostmsu added a commit to losttech/IO.Links that referenced this issue Feb 4, 2020
@lostmsu
Copy link

lostmsu commented Feb 4, 2020

Looks like the solution @danmosemsft proposed is not valid. I am using libc symlink call, and while GetLastWin32Error correctly returns EEXIST (17), simply adding 0x8007000 to it using the method suggested in GetHRForLastWin32Error and then passing result to ThrowExceptionForHR does not give a meaningful error message.

@janvorli
Copy link
Member

janvorli commented Feb 4, 2020

@lostmsu the GetLastWin32Error is supposed to return windows error codes, not the Unix ones. So the error code 17 is ERROR_NOT_SAME_DEVICE (if the code is in decimal). See https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-.

@lostmsu
Copy link

lostmsu commented Feb 4, 2020

@janvorli GetLastWin32Error is currently returning and possibly also cleaning errno on Linux, which is sort of what I expect it to do.

This issue though is about getting errno and a meaningful error message after a failed PInvoke call on Linux and MacOS. The code in question was proposed as a workaround, and it does not solve the problem.

I think the issue should be reopened.

@dotnet dotnet locked as resolved and limited conversation to collaborators Dec 25, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Runtime.InteropServices question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests

7 participants