web-dev-qa-db-fra.com

C # - Comment convertir un objet en IntPtr et inversement?

Je veux passer un objet du code managé à une fonction WinApi en tant que IntPtr. Il transmettra cet objet à ma fonction de rappel dans le code managé comme IntPtr. Ce n'est pas une structure, c'est une instance d'une classe.

Comment puis-je convertir object en IntPtr et vice versa?

41
Bitterblue

Donc, si je veux passer une liste à ma fonction de rappel via WinApi, j'utilise GCHandle

// object to IntPtr (before calling WinApi):
List<string> list1 = new List<string>();
GCHandle handle1 = GCHandle.Alloc(list1);
IntPtr parameter = (IntPtr) handle1;
// call WinAPi and pass the parameter here
// then free the handle when not needed:
handle1.Free();

// back to object (in callback function):
GCHandle handle2 = (GCHandle) parameter;
List<string> list2 = (handle2.Target as List<string>);
list2.Add("hello world");

Merci à David Heffernan

Edit: Comme indiqué dans les commentaires, vous devez libérer la poignée après utilisation. J'ai aussi utilisé le casting. Il pourrait être judicieux d'utiliser les méthodes statiques GCHandle.ToIntPtr(handle1) et GCHandle.FromIntPtr(parameter) comme ici . Je ne l'ai pas vérifié.

55
Bitterblue

Bien que la réponse acceptée soit correcte, je voulais y ajouter un peu.

J'ai adoré créer des extensions pour cela, il indique: list1.ToIntPtr().

public static class ObjectHandleExtensions
{
    public static IntPtr ToIntPtr(this object target)
    {
        return GCHandle.Alloc(target).ToIntPtr();
    }

    public static GCHandle ToGcHandle(this object target)
    {
        return GCHandle.Alloc(target);
    }

    public static IntPtr ToIntPtr(this GCHandle target)
    {
        return GCHandle.ToIntPtr(target);
    }
}

De plus, en fonction de ce que vous faites, il peut être intéressant de contenir votre liste dans un IDisposable.

public class GCHandleProvider : IDisposable
{
    public GCHandleProvider(object target)
    {
        Handle = target.ToGcHandle();
    }

    public IntPtr Pointer => Handle.ToIntPtr();

    public GCHandle Handle { get; }

    private void ReleaseUnmanagedResources()
    {
        if (Handle.IsAllocated) Handle.Free();
    }

    public void Dispose()
    {
        ReleaseUnmanagedResources();
        GC.SuppressFinalize(this);
    }

    ~GCHandleProvider()
    {
        ReleaseUnmanagedResources();
    }
}

Et puis vous pourriez le consommer comme ceci:

using (var handleProvider = new GCHandleProvider(myList))
{
    var b = EnumChildWindows(hwndParent, CallBack, handleProvider.Pointer);
}
7
Josh Gust