nicht modalen Dialog mit eigener Messagepump ausstatten



  • Dein Code ist ein Witz!

    Danke

    Wo muss ich denn das Fenster erzeugen? Kannst du mir nicht ne kleine Hilfestellung geben.


  • Mod

    Implementiere eine eigene CWinThread Klasse und dort im InitInstance Coe solltest Du Dein Fenster erzeugen...

    Werde Dir klar, welcher Code in welchem Thread ausgeführt wird!



  • Implementiere eine eigene CWinThread Klasse und dort im InitInstance Coe solltest Du Dein Fenster erzeugen...

    Habe ich doch gemacht: CMyThread ist abgeleitet von CWinThread

    Und in InitInstance Erzeuge ich meinen Dialog:

    BOOL CMyThread::InitInstance()
    {
    	if( m_dlgMy.GetSafeHwnd() == NULL)
    		m_dlgMy.Create(CMyDlg::IDD);
    
    	return CMyThread::InitInstance();
    }
    

    Nun möchte ich aber gerne den Dialog über eine Exportfunktion anzeigen oder ausblenden.


  • Mod

    Dann solltest Du direkt ShowWindow auf das Window Handle verwenden. Das geht zumindest aus jedem Thread.

    Besser ist Du kommunizierst mit dem neuen Thread/Fenster über Nachrichten.
    Sende also Deinem CWinThread eine Threadnachricht, die dann den Dialog anzeigt.



  • Ok Also habe nun folgendes festgestellt und gemacht.

    In InitInstance der App darf ich den Thread nicht erzeugen

    extern "C" DLLEXPIMP void Show(int time)
    {
    	// AFX_MANAGE_STATE-Makro
    	AFX_MANAGE_STATE(AfxGetStaticModuleState());
    
    	CTwinCCThread *thread = theApp.GetThread();
    
    	thread->ShowDialog();	
    }
    

    Nun erzeuge ich ihn wenn ich ihn zum ersten mal brauche

    CTwinCCThread* CTwinCCApp::GetThread()
    {
    	if( m_thread == NULL )
    	{
    		m_thread = (CTwinCCThread*)AfxBeginThread(RUNTIME_CLASS(CTwinCCThread));
    	}
    
    	return m_thread;
    }
    

    Das klappt soweit. Das mit ShowWindow dann schon wieder nicht mehr.

    void CTwinCCThread::ShowDialog()
    {
    	m_dlgTwincc.ShowWindow(SW_SHOW);
    }
    

    Nach OnInitDialog erhalte ich ein Debug Assertion Failed.

    Ab und zu kommt es auch vor dass InitInstance 2 mal aufgerufen wird.

    BOOL CMyThread::InitInstance()
    {
        if( m_dlgMy.GetSafeHwnd() == NULL)
            m_dlgMy.Create(CMyDlg::IDD);
    
        return CMyThread::InitInstance();
    }
    

    Einmal beim erzeugen des Threads und einmal beim ShowWindow


  • Mod

    Du kannst über Threadgrenzen hinweg die MFC Objekte nicht benutzen.
    Die Objekte benutzen eine Threadlokale Map. Jeder Zugriff auf eine Windowsfunktion des Objektes aus einem anderen Thread wird an dieser Stelle crashen.

    Ich habe nicht umsonst geschrioeben, Du sollst das HANDLE verwenden!
    Ich habe auch geschrieben, Du sollst private Nachrichten an den Thread versenden...
    Ich schreibe das nicht aus Spaß!



  • Ich schreibe das nicht aus Spaß!
    

    Glaub ich dir. Habe halt noch nicht alles auf anhieb verstanden. Sorry 😞

    Ich mach nochamls einen Versuch. Erst mal ShowWindow, bevor wir zu Nachrichten senden gehen. Will das alles ja verstehen. Du meinst ich soll statt diesem hier

    m_dlgTwincc.ShowWindow(SW_SHOW);
    

    das hier

    ::ShowWindow(m_dlgTwincc.GetSaveHWND(),SW_SHOW);
    

    verwenden

    Da passiert dann gar nichts. Habe ich da schon wieder was falsch verstanden. 😞

    Das liegt wohl daran dass InitInstance 2 mal aufgerufen wird. Oder?

    BOOL CMyThread::InitInstance()
    {
    if( m_dlgMy.GetSafeHwnd() == NULL)
    m_dlgMy.Create(CMyDlg::IDD);

    return TRUE;
    }



  • Nochmals richtig:
    statt

    m_dlgMy.ShowWindow(SW_SHOW);
    

    das hier

    ::ShowWindow(m_dlgMy.GetSafeHwnd(),SW_SHOW);
    


  • Hallo nochmal

    Könnte mir nochmals jemand helfen.

    Auf die folgende Frage habe ich auch noch keine Antwort erhalten: "Wo sollte ich dann RedrawWindow aufrufen? Aus meinem externen Applikation geht das nicht."


  • Mod

    Bei wem willst Du RedrawWindow ausführen?
    Und überhaupt warum?
    Was heißt "externe Applikation"? Hast Du jetzt auch noch Prozessgrenzen zu überwinden, oder wie sol ich das hier verstehen. Bisher haben wir nur von DLL und einem Priozess geredet, aber unterschiedliche Threads.

    Wenn eine Message-Pump läuft und Dein Fenster die neuen Informationen bekommt, werden die auch bei der nächsten WM_PAINT Nachricht im Fenster aktualisiert.



  • Bei wem willst Du RedrawWindow ausführen?
    Und überhaupt warum?

    Na das war doch dein Vorschlag!

    Martin Richter schrieb:

    Würde es nicht genügen wenn Du einfach einen RedrawWindow alle Naselang auf Deinen Dialog ausführst. Dann hast Du eine Fortschrittsanzeige, di sich auch bewegt...

    Was heißt "externe Applikation"? Hast Du jetzt auch noch Prozessgrenzen zu überwinden, oder wie sol ich das hier verstehen

    Die externe Applikation greift auf die Exportfunktionen der DLL zu.

    Das andere Problem dass ich ja noch hatte war dass CMyThread:InitInstance 2 mal aufgerufen wird.

    Habe das nun so gelöst:

    Bei meiner Threaderzeugung warte ich nun so lange bis der Dialog auch wirklich erzeugt ist:

    if( m_thread == NULL )
    {
      m_thread = (CMyThread*)AfxBeginThread(RUNTIME_CLASS(CMyThread));
      m_evtWaitForDlg.Lock();
    }
    
    BOOL CMyThread::InitInstance()
    {
    	if( m_dlgMy.GetSafeHwnd() == NULL)
    		m_dlgMy.Create(CMyDlg::IDD);
    
    	theApp.m_evtWaitForDlg.SetEvent();
    
    	//return CWinThread::InitInstance();
    
    	return TRUE;
    }
    

    so funktioniert das nun. Ob das schön ist weiß ich nicht


  • Mod

    RedrawWindow war meine Idee wenn Du nicht2 Threads verwendest.
    Wieso vermischt Du das jetzt hier?

    Wenn eine DLL in einen Prozess geladenwird, dannist diese immernoch Teil desselben Prozesses Punkt.

    Wenn InitInstance wirlich zweimal aufgerufen wird, dann wird die DLL auch zweimal geladen, oder geladen und entladen und erneut geladen.
    InitInstance ist in einer MFC DLL nichts anderes als der gewrappte DllMain Aufruf.

    Wenn irgdnweine Export Funktion aufgerufen wird weißt Du immer noch nicht aus welchem Thread das passiert.



  • RedrawWindow war meine Idee wenn Du nicht2 Threads verwendest.
    Wieso vermischt Du das jetzt hier?

    Ich vermisch das nicht. Ich weiß dass es zwei Themen sind. Mich interessieren beide Wege. Und sagte ja nur dass ich auf die Frage wo ich RedrawWindow aufrufen soll auch noch keine Antwort erhalten habe.

    Wenn InitInstance wirlich zweimal aufgerufen wird, dann wird die DLL auch zweimal geladen

    Es wird nicht InitInstance der DLL 2-mal aufgerufen sondern InitInstance der Threadklasse. Das habe ich aber genau dargestellt!

    y-vonne schrieb:

    Das liegt wohl daran dass InitInstance 2 mal aufgerufen wird. Oder?

    BOOL CMyThread::InitInstance()
    {
    if( m_dlgMy.GetSafeHwnd() == NULL)
    m_dlgMy.Create(CMyDlg::IDD);

    return TRUE;
    }


  • Mod

    1. Nein! Wenn Initinstance der Threadklasse zweimal aufgerufen wird, dann werden auch zwei Threads gestartet!... InitInstance wird für jeden Thread genau einmal aufgerufen.
    2. Wenn Du nur einen Thread hast, und Du nur einen Feedback geben möchtest (keinen Abbruch Button. etc.) dann baust Du eine loop mit z.b. 1Mio Iterationen. Alle 1000 Zyklen oder ausgerchenten 1% setzt Du in dem Progressbar neue Werte und erzwingst mit RedrawWindow einen Rferesh des Fensters. Es ist zwar nicht "bedienbar" aber es zeigt einen Fortschritt. Bedienbar ist es nicht weil es eben keine Messagepump gibt...

    Hinweis:
    Gefährlich ist es (kann es sein) im selben Thread in dieser Loops Messages zu pumpem. Dann wird das Programm reentrant. Soetwas sollte man tunlichst vermeiden.



    Du hast recht. InitInstance wird nur einmal aufgerufen. Das Problem liegt woanders. InitInstance wird nicht ganz zu Ende ausgeführt bevor die Funkion die den Thread anlegt endet. Und springt sozusagen danach wieder in Initinstance zurück.

    Trotzdem liegt hier das Problem. Der Dialog wird nicht fertig gestellt bevor der Threaderzeuger endet. Wenn ich wie gesagt warte bis der Dialog fertig erstellt wurde und dass dann dem Threaderzeuger über ein Event mitteile klappt das.

    Doch ich benötige eine Rückmeldung. Somit fällt das mit dem RedrawWindow weg.


  • Mod

    Was für ein Parent gibst Du denn an? Evtl. versucht Dein Dialog an Dein anderes Fenster eine Nachricht zu senden und steht.
    Mach doch mal einen Break-All in diesem Moment.

    Was macht Dein Worker-Thread?, Wo steht er im Callstack?



  • Was für ein Parent gibst Du denn an?

    Ist doch in meinen Beiträgen zu erkennen. Ich gebe gar kein Parent an.

    BOOL CMyThread::InitInstance()
    {
       if( m_dlgMy.GetSafeHwnd() == NULL)
          m_dlgMy.Create(CMyDlg::IDD);
    
       return TRUE;
    }
    

    Was macht Dein Worker-Thread?, Wo steht er im Callstack?

    In welchem Moment?


  • Mod

    1. Wenn Du kein Parent angibst, sucht sich die MFC evtl. eines.
    2. In dem Moment wo Du sagst, der Dialog erscheint erst, wenn der Thread fertig ist. Also in dem Moment in dem Du meinst hier müsste bereits eine Aktion passieren. Also bleibt vermutlich im Create stehen.



  • 1. Was bedeutet dass dann für den Fall. Muß ich ein Parent angeben?
    2. Ich erklär mal den Ablauf.

    Es wird die Exportfunktion Show aufgerufen

    extern "C" DLLEXPIMP void Show(int time)
    {
    	// AFX_MANAGE_STATE-Makro
    	AFX_MANAGE_STATE(AfxGetStaticModuleState());
    
    	CMyThread *thread = theApp.GetThread();
    	PostThreadMessage(thread->m_nThreadID,SW_SHOW,time,0);	
    }
    

    Dazu wird wenn nötig der Thread erzeugt.

    CMyThread* CMyApp::GetThread()
    {
    	if( m_thread == NULL )
    	{
    		m_thread = (CMyThread*)AfxBeginThread(RUNTIME_CLASS(CMyThread));
    		//m_evtWaitForDlg.Lock();
    	}
    
    	return m_thread;
    }
    

    Bei Threaderzeugung wird kurz in InitInstance gesprungen. Debugger steht bei der ersten geschweiften Klammer.

    BOOL CMyThread::InitInstance()
    {
    	if( m_dlgMy.GetSafeHwnd() == NULL)
    		m_dlgMy.Create(CMyCDlg::IDD);
    
    	//theApp.m_evtWaitForDlg.SetEvent();
    
    	//return CWinThread::InitInstance();
    
    	return TRUE;
    }
    

    Danach springt der Debugger wieder in die Funktion GetThread und arbeitet diese zu Ende. Jetzt wird wieder in Initinstance gesprungen und die nächste Zeile abgearbeitet. Danach springt der Debugger an die Stelle an der eine Nachricht an den Thread gesendet wird:

    PostThreadMessage(thread->m_nThreadID,SW_SHOW,time,0);
    

    Und wieder zurück zu InitInstance nächste Zeile abarbeiten.
    Danach endet die Exportfunktion und Initinstance wird auch zu Ende abgearbeitet.

    Die Message die gesendet wurde wird nicht abgefangen.

    Und wie gesagt warte ich bis der Dialog fertig erstellt wurde mit:

    m_evtWaitForDlg.Lock();
    

    und

    theApp.m_evtWaitForDlg.SetEvent();
    

    funktioniert das ganze.


  • Mod

    Du kannst so nicht sequentiell Debuggen. Die beiden Threads laufen parallel!

    Wo hast Du den Handler für die Threadmessage eingebaut?
    Threadmessage müssen im WinThread abgefangen werden. Siehe MSDN on_thread_message...

    Wieso sendest Du SW_SHOW als Nachricht. Das müsste sicherlich mit einer reservierten Nachricht kollidieren? Der Wert 5 steht für WM_SIZE!

    Sorry aber Dein Vorgehen kommt mir vor wie stochern im Nebel.


Anmelden zum Antworten