Methode eines Objekts als Callbackroutine



  • Moin.

    Ich verwende in einem Projekt dnm Multimedia Timer. Um diesen zu starten verwende ich die Funktion timeSetEvent.

    Diese brauch als Parameter eine Zeiger auf eine Callbackfunktion:

    [in] Pointer to a callback function that is called once on expiration of a single event or periodically on expiration of periodic events.

    Ich kann einen Zeiger auf eine static Methode eines Objects übergeben und alles funktioniert.

    Da ich in dieser Methode auf Felder und andere Methoden sowohl von diesem als auch von anderen Objekten zugreife, müssen all diese auch static sein/werden.

    Da mir das nicht gefällt habe ich mich gefragt, ob es eine Möglichkeit gibt, eine 'normale' nicht static Medthode als Callbackfunktion zu verwenden?



  • Du musst eine Globale oder statische (ist vorzuziehen) Funktion als Callback verwenden.

    Übergibt als dwUser Parameter einfach ein Zeiger auf die aufrufende Klasse, welche dir dann im Callback wieder zur Verfügung steht.

    Hier noch die Doku:
    http://msdn.microsoft.com/en-us/library/aa448195.aspx

    Simon



  • Okay, so kann ich zumindest auf die Methoden meiner Klasse zugreifen.

    Wenn ich allerdings in solch einer Methode dann auf eine nicht statische Variable meines Objekts zugreife, bekomme ich eine Zugriffsverletztung:

    ....
    ....
    private:
    	int m_MyInt;
        static void CALLBACK TimerExpired(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2);
    	void testFkt();
    
    ....
    ....
    
    void CmmTimer01Dlg::OnBnClickedButton1()
    {
    	MMRESULT Res;
    	TIMECAPS tc;
    	static int	wTimerID;
    
    	//--- Maximale Timerauflösung abfragen (WinCE 6 -> 1ms)
    	Res = timeGetDevCaps(&tc, sizeof(TIMECAPS));
    
    	//--- Timer auf maximale Auflösung stellen
    	if(Res == TIMERR_NOERROR)
    		Res = timeBeginPeriod(tc.wPeriodMin);
    
    	if(Res == TIMERR_NOERROR)
    	{
    		wTimerID = timeSetEvent(250,                                         // uDelay
    								0,                                          // uResolution
    								TimerExpired,                        // lpTimeProc
    								(DWORD)this,                                // dwUser
    								TIME_CALLBACK_FUNCTION | TIME_ONESHOT      // fuEvent
    								);
    
    		if(wTimerID != NULL)
    			Res = TIMERR_NOERROR;
        }
    
    	if(Res != TIMERR_NOERROR)
    		TRACE("Error\n");
    }
    
    void CALLBACK CmmTimer01Dlg::TimerExpired(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
    {
        TRACE("TimerExpired\n");
    
    	((CmmTimer01Dlg *)dwUser)->testFkt();
    }
    
    void CmmTimer01Dlg::testFkt()
    {
    	TRACE("testFkt #1\n");
    	m_MyInt = 0;
    	TRACE("testFkt #2\n");
    }
    

    Unbehandelte Ausnahme bei 0x0041306d in mmTimer01.exe: 0xC0000005: Zugriffsverletzung beim Schreiben an Position 0x00000078.



  • *push*



  • Wo und wie wird der Dialog instanziert?
    Lebt er genug lange? (Mindestens solange, bis der Timer abgelaufen ist?)

    Simon



  • Der Dialog lebt die ganze Zeit.

    Ich habe um das Problem zu isolieren eine 'Minimalprojekt' erstellt. Dabei habe ich beim Schritt Anwendungstyp des MFC-Anwendungs-Assistenten 'Auf Dialogfeldern basierend' gewählt.

    Der Dialog ist also quasi das Hauptfenster.



  • Hier mal ein minimal Bsp.
    Ev. hilfts Dir ja beim Fehler finden.

    #include <iostream>
    #include <stdexcept>
    
    #include <windows.h>
    
    #pragma comment (lib, "Winmm.lib")
    
    class Test
    {
    public:
        Test(UINT delay, UINT resolution)
            : timerId_(NULL)
        {
            timerId_ = timeSetEvent(delay, resolution, &Test::onTimeout, reinterpret_cast<DWORD_PTR>(this), TIME_ONESHOT | TIME_CALLBACK_FUNCTION);
    
            if (timerId_ == NULL)
            {
                throw std::runtime_error("Could not acquire timer");
            }
        }
    
        ~Test()
        {
            timeKillEvent(timerId_);
        }
    
    private:
        static void CALLBACK onTimeout(UINT /*uTimerID*/, UINT /*uMsg*/, DWORD_PTR dwUser, DWORD_PTR /*dw1*/, DWORD_PTR /*dw2*/)
        {
            Test* object = reinterpret_cast<Test*>(dwUser);
    
            object->onTimeout();
        }
    
        void onTimeout()
        {
            std::cout << "Test::onTimeout" << std::endl;
        }
    
    private:
        MMRESULT timerId_;
    };
    
    int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
    {
        Test test(2000, 0);
    
        std::cin.clear();
        std::cin.ignore(std::cin.rdbuf()->in_avail());
        std::cin.get();
    }
    

Log in to reply