Skip to content

SafeHandle marshalling improvements in the source-generated marshalling #73496

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

Closed
jkoritzinsky opened this issue Aug 5, 2022 · 3 comments
Closed

Comments

@jkoritzinsky
Copy link
Member

From #69608:

  1. SafeHandle disposal. When marshaling out a SafeHandle, the generator emits code like this:
            __retVal = new global::Microsoft.Win32.SafeHandles.SafeFindHandle();
            try
            {
                //
                // Pin
                //
                fixed (void* __lpFileName_gen_native__pinned = lpFileName)
                {
                    ushort* __lpFileName_gen_native = (ushort*)__lpFileName_gen_native__pinned;
                    fixed (global::Interop.Kernel32.WIN32_FIND_DATA* __lpFindFileData_gen_native = &lpFindFileData)
                    {
                        {
                            System.Runtime.InteropServices.Marshal.SetLastSystemError(0);
                            __retVal_gen_native = __PInvoke__(__lpFileName_gen_native, fInfoLevelId, __lpFindFileData_gen_native, fSearchOp, lpSearchFilter, dwAdditionalFlags);
                            __lastError = System.Runtime.InteropServices.Marshal.GetLastSystemError();
                        }
                    }
                }

                __invokeSucceeded = true;
            }
            finally
            {
                if (__invokeSucceeded)
                {
                    //
                    // GuaranteedUnmarshal
                    //
                    System.Runtime.InteropServices.Marshal.InitHandle(__retVal, __retVal_gen_native);
                }
            }

It's using a try/finally and __invokeSucceeded, so it's trying to accomodate something in that try region throwing. But if something there does throw, the allocated SafeHandle won't be disposed, which will in turn leave it for finalization. Should there be a catch block?

catch
{
    __retVal.Dispose();
    throw;
}

or potentially instead a catch with an exception filter that does the disposal and returns false to avoid interrupting the unwind?

In .NET 8, we'll look at moving the SafeHandle marshalling into a custom marshaller type. When we do so, we'll address this feedback.

Note for future self: This would be a good use case for the OnInvoked custom marshaller method that isn't a GC.KeepAlive usage.

@ghost
Copy link

ghost commented Aug 5, 2022

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

Issue Details

From #69608:

  1. SafeHandle disposal. When marshaling out a SafeHandle, the generator emits code like this:
            __retVal = new global::Microsoft.Win32.SafeHandles.SafeFindHandle();
            try
            {
                //
                // Pin
                //
                fixed (void* __lpFileName_gen_native__pinned = lpFileName)
                {
                    ushort* __lpFileName_gen_native = (ushort*)__lpFileName_gen_native__pinned;
                    fixed (global::Interop.Kernel32.WIN32_FIND_DATA* __lpFindFileData_gen_native = &lpFindFileData)
                    {
                        {
                            System.Runtime.InteropServices.Marshal.SetLastSystemError(0);
                            __retVal_gen_native = __PInvoke__(__lpFileName_gen_native, fInfoLevelId, __lpFindFileData_gen_native, fSearchOp, lpSearchFilter, dwAdditionalFlags);
                            __lastError = System.Runtime.InteropServices.Marshal.GetLastSystemError();
                        }
                    }
                }

                __invokeSucceeded = true;
            }
            finally
            {
                if (__invokeSucceeded)
                {
                    //
                    // GuaranteedUnmarshal
                    //
                    System.Runtime.InteropServices.Marshal.InitHandle(__retVal, __retVal_gen_native);
                }
            }

It's using a try/finally and __invokeSucceeded, so it's trying to accomodate something in that try region throwing. But if something there does throw, the allocated SafeHandle won't be disposed, which will in turn leave it for finalization. Should there be a catch block?

catch
{
    __retVal.Dispose();
    throw;
}

or potentially instead a catch with an exception filter that does the disposal and returns false to avoid interrupting the unwind?

In .NET 8, we'll look at moving the SafeHandle marshalling into a custom marshaller type. When we do so, we'll address this feedback.

Note for future self: This would be a good use case for the OnInvoked custom marshaller method that isn't a GC.KeepAlive usage.

Author: jkoritzinsky
Assignees: -
Labels:

area-System.Runtime.InteropServices

Milestone: 8.0.0

@AaronRobinsonMSFT
Copy link
Member

/cc @stephentoub

@jkoritzinsky
Copy link
Member Author

We completed this work with #85419

@ghost ghost locked as resolved and limited conversation to collaborators Jul 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants