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

Handle SetLastError=true #360

Merged

Conversation

elinor-fung
Copy link
Member

@elinor-fung elinor-fung commented Nov 17, 2020

  • Add functions for getting/setting system error (Get/SetLastSystemError) and setting P/Inovke error (SetLastWin32Error) to Ancillary.Interop
  • Update generated stub code to clear the system error before invoke, get the system error after and update the stored P/Invoke error

cc @AaronRobinsonMSFT @jkoritzinsky

Examples:

SetLastError=true, blittable signature
public static partial int SetError(int error, byte shouldSetError)
{
    unsafe
    {
        int __retVal = default;
        int __lastError = default;
        //
        // Invoke
        //
        {
            System.Runtime.InteropServices.MarshalEx.SetLastSystemError(0);
            __retVal = SetError__PInvoke__(error, shouldSetError);
            __lastError = System.Runtime.InteropServices.MarshalEx.GetLastSystemError();
        }

        System.Runtime.InteropServices.MarshalEx.SetLastWin32Error(__lastError);
        return __retVal;
    }
}

[System.Runtime.InteropServices.DllImportAttribute("NativeExportsNE", EntryPoint = "set_error")]
extern private static unsafe int SetError__PInvoke__(int error, byte shouldSetError);
SetLastError=true, non-blittable signature
public static partial string SetError_NonBlittableSignature(int error, bool shouldSetError, string errorString)
{
    unsafe
    {
        byte __shouldSetError_gen_native = default;
        ushort *__errorString_gen_native = default;
        string __retVal = default;
        ushort *__retVal_gen_native = default;
        int __lastError = default;
        //
        // Marshal
        //
        __shouldSetError_gen_native = (byte)(shouldSetError ? 1 : 0);
        //
        // Invoke
        //
        fixed (char *__errorString_gen_native__pinned = errorString)
        {
            System.Runtime.InteropServices.MarshalEx.SetLastSystemError(0);
            __retVal_gen_native = SetError_NonBlittableSignature__PInvoke__(error, __shouldSetError_gen_native, (ushort *)__errorString_gen_native__pinned);
            __lastError = System.Runtime.InteropServices.MarshalEx.GetLastSystemError();
        }

        //
        // Unmarshal
        //
        __retVal = __retVal_gen_native == null ? null : new string ((char *)__retVal_gen_native);
        System.Runtime.InteropServices.MarshalEx.SetLastWin32Error(__lastError);
        return __retVal;
    }
}

[System.Runtime.InteropServices.DllImportAttribute("NativeExportsNE", EntryPoint = "set_error_return_string")]
extern private static unsafe ushort *SetError_NonBlittableSignature__PInvoke__(int error, byte shouldSetError, ushort *errorString);

Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Looks like we found the owner for proposing these new APIs :-)

I assume we need/want:

  • SetLastSystemError(), even though we could P/Invoke into that ourselves - see example in PR.
  • SetLastWin32Error(), even though we could use reflection to get at the call - see example in PR.

@jkotas
Copy link
Member

jkotas commented Nov 21, 2020

        fixed (char *__errorString_gen_native__pinned = errorString)
        {
            System.Runtime.InteropServices.MarshalEx.SetLastSystemError(0);
            __retVal_gen_native = SetError_NonBlittableSignature__PInvoke__(error, __shouldSetError_gen_native, (ushort *)__errorString_gen_native__pinned);
            System.Runtime.InteropServices.MarshalEx.SetLastWin32Error(System.Runtime.InteropServices.MarshalEx.GetLastSystemError());
        }

        //
        // Unmarshal
        //
        __retVal = __retVal_gen_native == null ? null : new string ((char *)__retVal_gen_native);
        return __retVal;

Is the last error guaranteed to be left intact by all the unmarshalling code? Should SetLastWin32Error be called right before returning instead?

@elinor-fung
Copy link
Member Author

Should SetLastWin32Error be called right before returning instead?

Ahh, yeah. Good call.

@elinor-fung
Copy link
Member Author

Interestingly, the built-in system today can allow writing over the error code in unmarshalling (tried this quickly with a custom marshaler does a Console.WriteLine in its unmarashalling function).

@jkotas
Copy link
Member

jkotas commented Nov 21, 2020

Yes, the built-in system has a patch work of workarounds like this that I am sure has many holes.

@elinor-fung elinor-fung merged commit b36b0b8 into dotnet:feature/DllImportGenerator Nov 30, 2020
@elinor-fung elinor-fung deleted the setLastError branch November 30, 2020 18:41
jkoritzinsky pushed a commit to jkoritzinsky/runtime that referenced this pull request Sep 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-DllImportGenerator Source Generated stubs for P/Invokes in C#
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants