nicht modalen Dialog mit eigener Messagepump ausstatten



  • Hallo.

    Ich erzeuge mit Create aus meinem Hauptdialog einen neuen Unterdialog und zeige ihn mit ShowWindow an.

    Funktioniert auch so weit. Der Dialog ist nicht modal. Wenn ich nun aber in der Hauptdialog z.B. ein Sleep einbaue hängt auch der Unterdialog. Dies möchte ich nun verhindern.

    Das liegt ja nun daran, verbessert mich wenn ich falsch liege, dass der Unterdialog keine eigene Message Pump hat.

    Kann man das irgendwie einfach anlegen, oder muss ich dazu einen eigenen UI Thread anlegen?

    Und wenn ja was muss ich da genau machen?


  • Mod

    Das ist eben so. Ein nicht modaler Dialog hat keine eigene Message-Pump und nützt die Main-Pump.

    Wenn Du also keine Nachrichten abholst hängt der Dialog. Wenn Du also eine eigene Message-Loop einbaust ist der Diaog nicht mehr "nicht modal".
    Entweder hast Du das eine, oder das andere.

    Man kann natürlich auch einen eigenen Thread mit eigener Message-Loop erzeugen und in dieser den Dialog erzeugen...

    Was willst Du eigentlich?



  • Man kann natürlich auch einen eigenen Thread mit eigener Message-Loop erzeugen und in dieser den Dialog erzeugen...

    Ok und wie mache ich das. Ist das dann ein UI Thread?

    Was willst Du eigentlich?

    Ich habe eine Fremdapplikation nennt sich WinCC und ist von Siemens. Dort kann man einfaches c-programmieren. Ohne Threads oder sonstiges.

    Nun wird in diesem "Skript" eine Berechung durchgeführt und der Status soll über einen Dialog der nicht Modal ist ausgegeben werden. Hier gibt es nun die Möglichkeit eine c++ DLL einzubinden.

    Diese DLL beinhaltet also diesen nicht modalen Dialog. Doch wenn ich nun diesen Dialog aufrufe ist dieser überhaupt nicht flüssig.



  • Scheint nicht so trivial zu sein. Keiner mehr eine Idee?



  • Ok. Vieleicht mal eine Idee und mir kann dann jemand weiter helfen.

    Erzeuge nun einen UI Thread in einer Exportfunktion meiner DLL

    CMyThread* thread = (CMyThread*)AfxBeginThread(RUNTIME_CLASS(CMyThread));
    

    MyThread ist von CWinThread abgeleitet.

    In InitInstance von CMyThread erzeuge ich nun den nicht modalen Dialog.

    if( m_dlgMy.GetSafeHwnd() == NULL)
      m_dlgMy.Create(CTMyDlg::IDD);
    

    Wo rufe ich aber nun ShowWindow meines Dialogs auf??

    Oder ist das nicht der richtige Weg?


  • Mod

    Du rufst Den Show auch aus dem Thread auf, der den Dialog erzeugt hat.

    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...



  • Hallo.

    Danke für deine Antwort.

    Zeige mal hier Ausschnitte aus meinem Code

    extern "C" DLLEXPIMP void Show()
    {
    	// AFX_MANAGE_STATE-Makro
    	AFX_MANAGE_STATE(AfxGetStaticModuleState());
    
    	CTwinCCThread* thread = (CMyThread*)AfxBeginThread(RUNTIME_CLASS(CMyThread));
    	thread->ShowDialog();
    
    }
    
    void CMyThread::ShowDialog()
    {
    	m_dlgMy.ShowDialog();
    }
    
    void CMyDlg::ShowDialog()
    {
    
    	m_elapsedTimer.StartHPT();
    	m_elapsedTime.Format("%d min %d sec",0,0);
    
    	UpdateData(FALSE);
    
    	// Timer Event starten
    	m_nTimer = SetTimer(1,m_timerStep,0);
    
    	ShowWindow(SW_SHOW);
    }
    

    Wenn ich das ausführe wird bevor der Dialog überhaupt angezeigt wird, der Destruktor von CMyDlg ausgeführt.

    Und wenn ich die andere Lösung verfolge:

    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...

    Wo sollte ich dann RedrawWindow aufrufen? Aus meinem externen Applikation geht das nicht.


  • Mod

    Dein Code ist ein Witz.
    Dein Thread muss den Dialog anzeigen und er muss auch laufen.

    Du erzeugst kein Fenster in Deinem Thread. Du erzeugst einen Thread und dann wie bisher in Deinem aktuelenThread das Fenster...

    Bitte mach dich mit Threads vertraut...



  • 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.


Anmelden zum Antworten