it-swarm.com.de

Wie konvertiere ich ein Objekt nach IntPtr und zurück?

Ich möchte ein Objekt aus verwaltetem Code als IntPtr an eine WinApi-Funktion übergeben. Dieses Objekt wird im verwalteten Code als IntPtr an meine Rückruffunktion zurückgegeben. Es ist keine Struktur, es ist eine Instanz einer Klasse.

Wie konvertiere ich object in IntPtr und zurück?

41
Bitterblue

Wenn ich also über WinApi eine Liste an meine Rückruffunktion übergeben möchte, verwende ich 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");

Danke an David Heffernan

Bearbeiten: Wie in den Kommentaren angegeben, müssen Sie das Handle nach der Verwendung freigeben. Ich habe auch Casting benutzt. Es kann sinnvoll sein, die statischen Methoden GCHandle.ToIntPtr(handle1) und GCHandle.FromIntPtr(parameter) wie hier zu verwenden. Das habe ich nicht verifiziert.

55
Bitterblue

Während die akzeptierte Antwort korrekt ist, wollte ich etwas hinzufügen.

Ich habe es geliebt, Erweiterungen dafür zu erstellen, daher lautet sie: 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);
    }
}

Abhängig davon, wie viel Sie tun, kann es auch hilfreich sein, Ihre Liste in einem IDisposable zu speichern.

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();
    }
}

Und dann könnten Sie es so konsumieren:

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