template class SafeGCHandle (Niantic.ARDK.Utilities.SafeGCHandle)

Overview

This class has the purpose of allowing C# objects to be passed to native code as a “handle” and then obtained back when native code calls back into C#. This is effectively the same purpose as the GCHandle struct, yet this one is somewhat safer, as it will consistently return null if we try to get a value of a freed handle, while the one from .NET might return null, might throw an exception or, even worse, might return an invalid value. More…

template <T>
class SafeGCHandle: IEquatable< SafeGCHandle< T >> {
public:
    // methods

    static SafeGCHandle<T> Alloc< T >(T instance);
    static IntPtr AllocAsIntPtr(object instance);
    static bool Free(IntPtr id);
    static SafeGCHandle<T> FromIntPtr(IntPtr id);
    static T TryGetInstance< T >(IntPtr id);
    static object TryGetUntypedInstance(IntPtr id);
    override bool Equals(object obj);
    bool Equals(SafeGCHandle<T> other);
    bool Free();
    override int GetHashCode();
    IntPtr ToIntPtr();
    T TryGetInstance();
};

Detailed Documentation

This class has the purpose of allowing C# objects to be passed to native code as a “handle” and then obtained back when native code calls back into C#. This is effectively the same purpose as the GCHandle struct, yet this one is somewhat safer, as it will consistently return null if we try to get a value of a freed handle, while the one from .NET might return null, might throw an exception or, even worse, might return an invalid value.

Represents a type-safe, SafeGCHandle, that can be used to either get access to the instance again, to obtain its IntPtr ID, or to free the handle.

This struct is marshalled just as the IntPtr ID it contains, so [DllImport] methods that need to receive or return handles of a particular type can request for a SafeGCHandle<SomeType> instead of requesting just for an IntPtr.

Methods

static SafeGCHandle<T> Alloc< T >(T instance)

Allocates a new typed safe handle for the given instance. Such returned object is just a struct and it will not release the object if it goes out-of-scope. This is on purpose, as the intended use is to pass such handle to the native side and then allow the local one to go out of scope. At a later point, the instance might be obtained by calling TryGetInstance or another SafeGCHandle<T> can be reconstructed from its IntPtr representation by calling SafeGCHandle<T>.FromIntPtr(handleValue). The handle needs to be explicitly freed when we don’t need to hold a reference to the instance anymore by calling Free() (either from the typed SafeGCHandle or by giving its IntPtr value).

Parameters:

instance

  • The instance to create a handle to. If null is provided, a handle with ID 0 will be returned.

Returns:

a SafeGCHandle to access this instance again.

static IntPtr AllocAsIntPtr(object instance)

Allocates a new handle for the given value, which guarantees that the object is kept alive even if there are no other references, and returns it just as an IntPtr. A future call to Free needs to be made, or there will be memory leaks.

Parameters:

instance

  • The instance to create a handle for. null is a valid argument and will just return the handle 0 (IntPtr.Zero).

Returns:

An IntPtr value that can be used to locate this instance again.

static bool Free(IntPtr id)

Tries to free the handle identified by the given ID.

Parameters:

id

  • The ID of the handle to free.

Returns:

True if such ID existed and was freed, false if it wasn’t valid (either because it never was or because it was being previously freed).

static SafeGCHandle<T> FromIntPtr(IntPtr id)

Instantiates a new SafeGCHandle<T> from a provided IntPtr ID. This action never fails but if the ID is invalid, a future call to TryGetInstance() will return null.

Parameters:

id

  • The IntPtr ID that represents the allocated handle.

static T TryGetInstance< T >(IntPtr id)

Tries to get an instance referenced by its handle ID as the given generic T type. T must be a class and null will be returned if either the result is null or the given T type is incorrect.

Parameters:

id

  • The IntPtr ID from the allocated handle that can be used to find the instance again.

Returns:

Either the found instance or null.

static object TryGetUntypedInstance(IntPtr id)

Tries to get the instance of a previously allocated handle by the handle ID. This method doesn’t care about the actual type of the instance and will return it just as “object”.

Parameters:

id

  • The IntPtr ID of the allocated handle to access to the instance. This is obtained by calling AllocAsIntPtr of by calling Alloc to get a handle and then calling ToIntPtr() on the handle.

Returns:

Either the instance as object, or null if the id is either invalid or the handle was already released.

override bool Equals(object obj)

Compares this handle to any object. It can only return true if obj is actually another SafeGCHandle of the same T type and pointing to the same ID.

Returns:

true if the provided obj is a SafeGCHandle of the same type and referencing the same ID. Note that two SafeGCHandles with different IDs that point to the same instance are still considered different, as each one of them need to be Free()d separately.

bool Equals(SafeGCHandle<T> other)

Checks if the provided handle equals this one. This means both have the same ID. Two different IDs can point to the same instance, but they will still return false in this check, as each one of them need to be Free()d independently.

Returns:

True if the IDs of both handles match, false otherwise.

bool Free()

Tries to free the effective handle this struct represents.

Returns:

Either true if the handle was valid and was deallocated, or false if the handle was somehow invalid (either already deallocated or never allocated).

override int GetHashCode()

Returns the hashcode of this handle, which is effectively the hashcode of its IntPtr ID.

Returns:

The hashcode of the ID of this handle.

IntPtr ToIntPtr()

Gets the inner IntPtr ID of this safe-handle.

Returns:

The IntPtr ID of this safe-handle.

T TryGetInstance()

Tries to get the instance represented by this safe-handle, already cast to its right type.

Returns:

Either the typed-instance or null (if the handle is invalid, deallocated or of the wrong type).