Objekte die mit CoCreateInstance erzeugt wurden in mehreren Klassen nutzen



  • Oha, warum? Da ist man doch mehr mit dem Framework als mit dem eigentlichen Programmieren beschäftigt.



  • @makopo
    Also das Problem ist dass ich nicht ganz verstehe was du da machen willst.
    Grundsätzlich lässt sich CComPtr aber durchaus schön verwenden um COM Objekte zu verwalten. Also das Ding kümmert sich darum korrekt AddRef und Release aufzurufen und das alles.

    CComPtr funktioniert aber natürlich nur mit COM Objekten.



  • Ich weiß nicht ob ich es verständlicher als im ersten Beitrag erklären kann, versuche es aber nochmal. Ich erstelle in der main-Funktion mehrere Objekte über die ich Funktionen der einzelnen Klassen aufrufen möchte. Aktuell erhalte ich in der Klasse ControlVideo beim debuggen die Meldung das m_deckLink den Wert Null hat, was zum Programmabbruch führt. Dies passiert in Zeile 4 des zweites Codeblocks im ersten Post. Letztlich ist das ja aber auch logisch, weil IDeckLink* deckLink ohne Wert initialisiert ist.

    Also scheint die Frage zu sein, wie kann deckLink ein anderer Wert als Null zugewiesen werden? Ich habe das jetzt so verstanden, das dies über CoCreateInstance() und der Initialisierung des COM-Objekts erfolgt (in diesem Fall DeckLinkIterator).

    Aber wie bekomme ich das umgesetzt? Quiche-Lorraine meinte, man kann das über eine abstrakte Klasse lösen. Das würde aber doch bedeuten das ich das Interface in jeder weiteren Klasse einbinden muss. Und das ist komplett konträr zum Beispielcode in der Entwicklerdoku. Dort gibt es eine Funktion die das COM-Objekt erstellt und diese Funktion ebenfalls in der main aufruft:

    HRESULT GetDeckLinkIterator(IDeckLinkIterator **deckLinkIterator)
    {
    	HRESULT result = S_OK;
    
    	// Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
    	result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)deckLinkIterator);
    	if (FAILED(result))
    	{
    		fprintf(stderr, "A DeckLink iterator could not be created.  The DeckLink drivers may not be installed.\n");
    	}
    
    	return result;
    }
    

    Das COM-Objekt wird aber scheinbar nicht an die Klassen weitergegeben, wie beispielsweise in dem folgenden Konstruktor zu sehen ist. Dieser Konstruktor ist ähnlich aufgebaut wie mein Konstruktor wie mein Konstruktor in der Klasse ControlVideo.

    DeckLinkInputDevice::DeckLinkInputDevice(IDeckLink* device)
    	: m_deckLink(device), m_deckLinkInput(NULL), m_cancelCapture(false), m_refCount(1)
    {
    	m_deckLink->AddRef();
    }
    

    Mich vewirrt das in der Entwicklerdoku im Grunde einfach nur steht, das man in der main-Methode einfach nur die Funktion CoCreateInstance() aufrufen soll und fertig. Er scheint ja aber doch noch ein wenig mehr dazu zu gehören. Oder irre ich mich wieder? 😅



  • @makopo sagte in Objekte die mit CoCreateInstance erzeugt wurden in mehreren Klassen nutzen:

    Ich weiß nicht ob ich es verständlicher als im ersten Beitrag erklären kann, versuche es aber nochmal. Ich erstelle in der main-Funktion mehrere Objekte über die ich Funktionen der einzelnen Klassen aufrufen möchte. Aktuell erhalte ich in der Klasse ControlVideo beim debuggen die Meldung das m_deckLink den Wert Null hat, was zum Programmabbruch führt. Dies passiert in Zeile 4 des zweites Codeblocks im ersten Post. Letztlich ist das ja aber auch logisch, weil IDeckLink* deckLink ohne Wert initialisiert ist.

    Ja, klar.

    Also scheint die Frage zu sein, wie kann deckLink ein anderer Wert als Null zugewiesen werden? Ich habe das jetzt so verstanden, das dies über CoCreateInstance() und der Initialisierung des COM-Objekts erfolgt (in diesem Fall DeckLinkIterator).

    Aber wie bekomme ich das umgesetzt?

    Naja ... ich weiss halt nicht wo der DeckLinkIterator herkommen soll. Ein Iterator entsteht normalerweise nicht aus dem Nichts, den bekommt man normalerweise von dem Ding über das man drüber iterieren möchte.
    Das kann dann so einfach sein wie

    HRESULT hr = ding->GetIterator(&m_deckLink);
    if (FAILED(hr)) { /*handle error*/ }
    

    Quiche-Lorraine meinte, man kann das über eine abstrakte Klasse lösen. Das würde aber doch bedeuten das ich das Interface in jeder weiteren Klasse einbinden muss.

    Ich müsste jetzt den ganzen Thread lesen um zu verstehen wie das gemeint war. Aber ich würde sagen: vergiss das erstmal.

    Und das ist komplett konträr zum Beispielcode in der Entwicklerdoku. Dort gibt es eine Funktion die das COM-Objekt erstellt und diese Funktion ebenfalls in der main aufruft:

    HRESULT GetDeckLinkIterator(IDeckLinkIterator **deckLinkIterator)
    {
    	HRESULT result = S_OK;
    
    	// Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
    	result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)deckLinkIterator);
    	if (FAILED(result))
    	{
    		fprintf(stderr, "A DeckLink iterator could not be created.  The DeckLink drivers may not be installed.\n");
    	}
    
    	return result;
    }
    

    OK, das sieht halbwegs vernünftig aus. Wenn der DeckLinkIterator implizit weiss worüber er drüber iterieren soll, dann sollte das so passen.

    Das COM-Objekt wird aber scheinbar nicht an die Klassen weitergegeben, wie beispielsweise in dem folgenden Konstruktor zu sehen ist. Dieser Konstruktor ist ähnlich aufgebaut wie mein Konstruktor wie mein Konstruktor in der Klasse ControlVideo.

    DeckLinkInputDevice::DeckLinkInputDevice(IDeckLink* device)
    	: m_deckLink(device), m_deckLinkInput(NULL), m_cancelCapture(false), m_refCount(1)
    {
    	m_deckLink->AddRef();
    }
    

    Ich seh in dem Beispiel keinen Iterator. Da wird ein IDeckLink übergeben. IDeckLink != IDeckLinkIterator.

    Mich vewirrt das in der Entwicklerdoku im Grunde einfach nur steht, das man in der main-Methode einfach nur die Funktion CoCreateInstance() aufrufen soll und fertig. Er scheint ja aber doch noch ein wenig mehr dazu zu gehören. Oder irre ich mich wieder? 😅

    Also...
    Um COM zu verwenden musst du als erstes mal CoInitializeEx aufrufen. Und danach kannst du COM Objekt erzeugen. Eine Möglichkeit dazu ist CoCreateInstance. Damit kann man quasi ein COM Objekt "aus dem Nichts" erzeugen. Für DeckLinkIterator funktioniert das wohl so.

    Viele COM Schnittstellen bieten dann aber auch Funktionen an um COM Objekte zu erzeugen. IDeckLinkIterator::Next scheint genau so eine Funktion zu sein. Das erzeugt dir ein DeckLink Objekt und gibt dir nen IDeckLink zurück. Zumindest sehen die Beispiele die ich im Netz finde sehr danach aus.

    Und dann kommt es halt einfach drauf an was du jetzt mit dem Ding weiter machen willst.



  • @hustbaer

    @hustbaer sagte in Objekte die mit CoCreateInstance erzeugt wurden in mehreren Klassen nutzen:

    Naja ... ich weiss halt nicht wo der DeckLinkIterator herkommen soll. Ein Iterator entsteht normalerweise nicht aus dem Nichts, den bekommt man normalerweise von dem Ding über das man drüber iterieren möchte.

    Ich hätte vielleicht schreiben sollen, das ich das Programm mit einer Library (DeckLink SDK) für Videohardware programmiere und das der dort über ein Interface bereitgestellt wird.

    Ich denke ich habe den Fehler gefunden. Ich hatte Next() in einer separaten Funktion aufgerufen, weil dort noch alle am PC angeschlossenen DeckLink Geräte ausgegeben werden. In der main habe ich den DeckLinkIterator aber null zugewiesen, so dass der Wert aus CoCreateInstance denke ich mal überschrieben wurde.
    Ich rufe den Code aus den einzelnen Funktionen nun direkt in der int main() auf und es funktioniert. Die deckLink Variable ist nicht mehr Null.

    So sieht es jetzt aus:

    int main() {
    	HRESULT result = S_OK;
    	CComPtr<IDeckLink> deckLink = nullptr;
    	CComPtr<IDeckLinkIterator> deckLinkIterator = nullptr;
    
    	//initializing COM
    	if (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) {
    		std::cout << "COM is initialized" << std::endl;
    	}
    
    //#### HRESULT InitDeckLink(IDeckLinkIterator* deckLink) {####
    	if (SUCCEEDED(CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&deckLinkIterator))) {
    		std::cout << "DeckLink Driver is installed." << std::endl;
    	}
    	else {
    		std::cout << "DeckLink Driver is not installed." << std::endl;
    	}
    
    // #### HRESULT PrintDevice(IDeckLinkIterator* deckLinkIterator, IDeckLink* deckLink) ####
    	while (deckLinkIterator->Next(&deckLink) == S_OK) {
    
    		BSTR modelName;
    		StringConverter sc;
    
    		int numDevices = 0;
    		++numDevices;
    
    		//prints detected devices to the console
    		if (deckLink->GetModelName(&modelName) == S_OK) {
    			std::cout << "Detected devices: " << numDevices << std::endl;
    			std::cout << sc.convertBSTR(modelName) << std::endl;
    		}
    		deckLink = nullptr;
    	}
    	deckLinkIterator = nullptr;
    
    	return result;
    
    //InitDeckLink(deckLinkIterator); // Aufruf InitDeckLink mit CoCreateInstance()
    //PrintDevice(deckLinkterator, deckLink); // Aufruf PrintDevice Funktion mit Next() 
    
    	auto control = std::make_unique<ControlVideo>(deckLink);
    	control->Feedback(); //testfunktion
    	
    	CoUninitialize();
    
    	return 0;
    }
    

    Ich finde es so ziemlich hässlich, weil ich gerne für jede Funktion eine Methode hätte, aber so geht es erstmal. Wenn es daran liegt ein ziemlich peinlicher und frustierender Fehler der mich Tage gekostet hat... 😅



  • Öhm du kannst doch die IDeckLinkIterator instanz als parameter an die Funktionen übergeben.



  • @makopo
    Ich würde erstmal das Erzeugen des DeckLink COM Objekts in eine Hilfsfunktion auslagern:

    CComPtr<IDeckLink> CreateDeckLinkInstance() {
    ...
    }
    

    Das ganze DeckLinkIterator Zeugs wird dann nur innerhalb dieser Funktion benötigt.
    Diese Funktion kannst du dann in main einfach aufrufen wo du das ControlVideo Dings erzeugst.

    	HRESULT const hrCoInit = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    	if (FAILED(hrCoInit)) {
    		std::cout << "COM initialization failed: " << hrCoInit << std::endl;
    		return hrCoInit;
    	}
    
    	auto control = std::make_unique<ControlVideo>(CreateDeckLinkInstance());
    	control->Feedback();
    	// ...
    

    Und nochmal zu dem Code den du gezeigt hast: Du sagst der funktioniert, aber der kann gar nicht funktionieren. Du hast da ja ein return result; drinnen bevor die ControlVideo Instanz erzeugt wird. -> Bin verwirrt.



  • @hustbaer

    Ja, stimmt, so funktioniert der Code nicht. Ich weiß auch nicht mehr wie das return result da reingekommen ist. Getestet hatte ich es aber ohne und so läuft es dann ja.

    Danke für deinen Hinweis die Hilfsfunktion direkt an ControlVideo zu übergeben. Da wäre ich so gar nicht drauf gekommen... 🙈



  • Hallo,

    ich habe noch eine Frage zum Thema.
    Bisher hatte ich in der main-Methode zentral eine DeckLink Instanz mit CoCreateInstance erzeugt. Hat super funktioniert. Nun erstelle ich mit Qt die GUI und die Initialisierung der DeckLink Instanz in der main macht in meinen Augen keinen Sinn mehr.
    Statt die Objekte von z.B. ControlVideo in der main zu erzeugen, mache ich das jetzt in den einzelnen Funktionen. Ich habe z.B. eine Funktion QStartVideo(), in der ich ein Objekt von ControlVideo erstelle und damit die Funktion StartVideo() aufrufe, welche den Stream startet. ControlVideo übergebe ich hier meine Hilfsfunktion CreateDeckLinkInstance(), um StartVideo() mit der DeckLink Instanz zu initialisieren.

    StartVideo:

    HRESULT ControlVideo::StartVideo(BMDDisplayMode displayMode, BMDPixelFormat pixelFormat,
    	BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType,
    	unsigned int channelCount, bool enableFormatDetection) {
    
    	HRESULT result = S_OK;
    
    	if (m_deckLinkInput->EnableVideoInput(displayMode, pixelFormat, videoInputFlags) == S_OK) {
    		std::cout << "Video input is enabled." << std::endl;
    	}
    
    	if (m_deckLinkInput->StartStreams() == S_OK) {
    		std::cout << "Stream has started." << std::endl;
    	}
    	else {
    		std::cout << "Something goes wrong in the startVideo method.";
    	}
    
    	return result;
    }
    

    QStartVideo:

    void QVideoMeter::QStartVideo() {
    
        CComPtr<IDeckLinkIterator> deckLinkIterator = nullptr;
        CComPtr<IDeckLink> deckLink = nullptr;
        CComPtr<InitializeDeckLink> init;
        
        auto control = std::make_unique<ControlVideo>(init->CreateDeckLinkInstance(deckLinkIterator, deckLink));
        
        //'p' avoid the call of Release()
        while (deckLinkIterator->Next(&deckLink.p) == S_OK) {
    
            HRESULT inputVideoData = control->StartVideo(bmdModeHD1080p50, bmdFormat8BitYUV, bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2, false);
    
            if (inputVideoData == S_OK) {
                ui.startBtn->setText("Running");
            }
        }
    }
    

    Hilfsfunktion:

    CComPtr<IDeckLink> InitializeDeckLink::CreateDeckLinkInstance(CComPtr<IDeckLinkIterator> deckLinkIterator, CComPtr<IDeckLink> deckLink) {
    
    	if (FAILED(CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&deckLinkIterator))) {
    		std::cout << "DeckLink Driver is not installed." << std::endl;
    	}
    	return deckLink;
    }
    

    Problem was ich sehe: wenn ich in der Hilfsfunktion deckLink zurückgebe, dies in QStatVideo aber mit NULL initialisiert wird, kann das Erzeugen der DeckLink Instanz nicht funktioniert oder? Denn wenn ich das Programm ausführe wird eine Exception ausgelöst und es kommt die Fehlermeldung das deckLinkInput NULL ist.

    Konstruktor ControlVideo:

    /**
    * Contructor with initializer list
    */
    ControlVideo::ControlVideo(CComPtr<IDeckLink> deckLink)
    	: m_deckLink(deckLink), m_deckLinkInput(deckLink),
    	m_inputCallback(nullptr), m_referenceCounter(1) {
    
    	if (!m_deckLink) {
    		throw std::runtime_error("No DeckLink Interface found.");
    	}
    
    	if (!m_deckLinkInput) {
    		throw std::runtime_error("No DeckLink Input Interface found.");
    	}
    }
    

    Hat jemand einen Tipp? Ich stehe da leider komplett auf dem Schlauch.



  • Habe das Problem durch die folgende Anpassung der Hilfsfunktion behoben:

    CComPtr<IDeckLink> InitializeDeckLink::CreateDeckLinkInstance() {
    
        CComPtr<IDeckLink> deckLink = nullptr;
        CComPtr<IDeckLinkIterator> deckLinkIterator = nullptr;
    
        HRESULT result = S_OK;
        result = CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&deckLinkIterator);
    
        if (FAILED(result)) {
            std::cout << "DeckLink Driver is not installed." << std::endl;
            QMessageBox::warning(nullptr, "DeckLink Treiber nicht installiert", "Bitte installieren Sie den aktuellen DeckLink Treiber von Blackmagic.");
        }
    
        if (deckLinkIterator->Next(&deckLink) != S_OK) {
            std::cout << "No DeckLink device detected." << std::endl;
            QMessageBox::warning(nullptr, "Kein Gerät erkannt", "Bitte verbinden Sie Ihren PC mit geeigneter Blackmagic Hardware (z.B. UltraStudio Recorder).");
        }
        return deckLink;
    
    }
    

Anmelden zum Antworten