4. April 2011

gemanagte Referenzen in einer nicht gemanagten Klasse speichern


Wenn man C++ CLI und normales C++ in einem Projekt zusammen einsetzt, tauchen früher oder später scheinbar unlösbare Probleme auf. Bei einem Videoplayer den wir zur Zeit programmieren, war es nun soweit.
Um den Programmcode etwas zu entschlacken und den darunter liegenden Algorithmus zur Erstellung eines DirectShow Aufnahmegraphen wieder sichtbar zu machen, wurde es endlich Zeit eine Referenz auf ein gemanagtes Objekt in einer nativen Klasse zu speichern.
Im WPF-Frontend (C#) stellt der Nutzer ein, von welcher Kamera er aufnehmen will und in welches Format das geschehen soll. Diese Konfiguration wird an das Backend (C++) übergeben, in dem schließlich der Aufnahmegraph aufgebaut wird. Dabei kann es sehr viele unterschiedliche Kombination der Konfigurationsparameter geben. Die Art der Kamera (DV, USB, …), die Audioquelle, TimeShift (darüber werde ich bei Gelegenheit auch noch einen Blog-Eintrag schreiben => das war meine Diplomarbeit!) und noch einige mehr spielen hierbei eine Rolle.
Der Aufbau des Aufnahmegraphen erfolgte in einer großen Klasse mit etlichen Funktionen, und immer wieder wurde als Parameter dieser Funktionen ein Parameter vom Typ ICaptureConfiguration^ übergeben. Also habe ich das ganze aufgeflochten und in mehrere Klassen verteilt. Eine abstrakte Basisklasse mit statischen Hilfsfunktionen und einem Interface mit den wichtigsten Methoden, welche dann durch die Detail-Klassen abgedeckt sein müssen. Und um nicht ständig die Konfiguration als Parameter übergeben zu müssen, wird dieser Kontraktur als Member gespeichert.
Doch oh Schreck, was ist das: “Der Typ ICaptureConfiguration^ kann nicht als Membervariable einer nicht C++ CLI Klasse verwendet werden!” Ist ja eigentlich auch logisch, wie soll sonst der Speichermanager der CLR mitbekommen, wann die Referenz nicht mehr gebraucht wird und er das Objekt wieder freigeben kann. Oder noch schlimmer, wenn der Speichermanager des Objekt in seinem Speicher an eine andere Adresse schiebt um Platz zu schaffen, wäre die Referenz plötzlich ungültig! Es gibt nun nur zwei Möglichkeiten: entweder alles auf C++ CLI umstellen, was mindesten 100 Tage oder mehr dauern würde oder aber doch einen Weg finden, die Referenz zu speichern.
Dank google, der MSDN und anderen Seiten ist es mir zum Glück schließlich doch gelungen die Referenz zu speichern. Genau dafür gibt es nämlich das Template gcroot. Es sorgt dafür, dass die zugewiesene Referenz immer erreichbar ist. Da man solch eine Referenz immer mit delete wieder freigeben müsste, ist es besser man nutzt auto_gcroot. Das wird nämlich wie alle Wrapper dieser Art automatisch freigegeben. Im Code sieht das ganze dann in etwa so aus:
#include 
#using 

using namespace System;
...

class CCaptureMgrBase
{
    CCaptureMgrBase(ICaptureConfiguration^ captureConfig)
    {
        m_captureConfig = captureConfig;
    }
    ...
    msclr::auto_gcroot m_captureConfig;
}
Mit –> kann man nun ganz normal auf die Referenz zugreifen und Funktionen aufrufen.
Eine kleine Anmerkung habe ich allerdings noch. Man kann m_captureConfig nicht mehr einfach so an eine Funktion übergeben, welche ein ICaptureConfiguration^ als Parameter erwarte! Dafür muss man nunm_captureConfig.get() aufrufen.
class CCaptureMgrBase
{
    ...
    void Render()
    {
        // geht nicht:
        CCaptureMgrBase::Helper(m_captureConfig);

        // so ist es richtig
        CCaptureMgrBase::Helper(m_captureConfig.get());
    }

    static void Helper(ICaptureConfiguration^ captureConfig) { ... }
}

Beitrag "gestohlen" mit freundlicher Unterstützung vom CPlusSharp Blog

Vielen Dank, mit freundlichen Grüßen
Euer incredible Leitman

Keine Kommentare: