Childwindows im Nachhinein einfügen



  • Hallo,
    ich habe ein Parentfenster mit CreateWindow() und einem eigenen Thread in dem die Nachrichtenschleife läuft erstellt und will nun hinterher weitere Childs einfügen.

    Die einzige Lösung die ich bisher gefunden habe, ist einen neuen thread mit neuer nachrichtenschleife aufzumachen in dem das Child erstellt wird, weil das Parent sonst nicht mehr reagiert.

    Das muss doch aber irgendwie effektiver gehen oder? ich kann doch jetzt nicht für jedes neue Child einen neuen Thread aufmachen?

    hier mal mein Code:

    erstellen des Parents in neuem Thread mit WndProc:

    HINSTANCE hi3;
    HWND parent;
    
    LRESULT CALLBACK WndProc3(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
    {
    	return DefWindowProc(hwnd, message, wparam, lparam);
    }
    
    DWORD WINAPI dateifenster(LPVOID IpParameter)
    {
    	WNDCLASS wc;
    
    	wc.cbClsExtra=0;
    	wc.cbWndExtra=0;
    	wc.hbrBackground=(HBRUSH) GetStockObject(WHITE_BRUSH);
    	wc.hCursor=LoadCursor(hi3, IDC_ARROW);
    	wc.hIcon=LoadIcon(hi3, IDI_WINLOGO);
    	wc.hInstance=hi3;
    	wc.lpfnWndProc=WndProc3;
    	wc.lpszClassName=TEXT("Dateifenster");
    	wc.lpszMenuName=0;
    	wc.style=CS_HREDRAW|CS_VREDRAW;
    
    	RegisterClass(&wc);
    
    	parent=CreateWindow(TEXT("Dateifenster"), TEXT("Dateimanager"), WS_OVERLAPPEDWINDOW, 100, 100, 600, 600, 0, 0, hi3, 0);
    	UpdateWindow(parent);
    	ShowWindow(parent, SW_NORMAL);
    
    	MSG msg;
    	while(GetMessage(&msg, 0, 0, 0))//Nachrichtenschleife
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return msg.wParam;
    }
    
    int main() 
    { 
    
    	DWORD id;
    	HANDLE h=CreateThread(0, 0, dateifenster, 0, 0, &id);
    
    	WaitForSingleObject(h, INFINITE);
    }
    

    nun die einzige mir bekannte Möglichkeit ein Child einzufügen:

    HWND button;
    
    DWORD WINAPI thread(LPVOID IpParameter)
    {
    
    	button=CreateWindowEx(WS_EX_TRANSPARENT, TEXT("BUTTON"), TEXT("hi"), BS_PUSHBUTTON|WS_VISIBLE|WS_CHILD|WS_EX_TRANSPARENT, 200, 200, 100, 40, parent, (HMENU) 5, hi3, 0);
    
    	UpdateWindow(button);
    	ShowWindow(button, SW_NORMAL);
    
    	MSG msg;
    	while(GetMessage(&msg, 0, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return msg.wParam;
    
    }
    
    void child_einfügen()
    {
    DWORD id;
    HANDLE h=CreateThread(0, 0, thread, 0, 0, &id);
    }
    

    Aber dabei muss ich eben immer einen neuen Thread aufmachen 😞

    auf Dauer werden das sehr viele Threads... da gibt es doch sicher eine bessere Möglichkeit in der ich nicht für jedes Child einen Thread öffnen muss?

    lg,
    andi01.



  • Warum lässt du deinen Primärthread nur auf den erstellten warten? Ich meine, welchen Sinn macht das?
    Lass doch deinen Primärthread das Hauptfenster erstellen und sich um die Verwaltung (Nachrichtenschleife etc.) kümmern. Dieser Thread ist auch für das Hinzufügen von neuen Childs verantwortlich. Irgendwelche später erstellte Threads können sich dann um andere Dinge kümmern, die Fensterverwaltung würde ich denen aber nicht überlassen. Oder gibt es einen speziellen Grund, das anders zu machen?

    Wenn, dann kann auch jedes neu erstellte Child seinerseits einen neuen Thread starten. Ein eigener Thread pro Child sollte jedoch optional sein.


  • Mod

    Man kann in einem anderen Thread zwar Fenster erzeugen, doch ist das eigentlich Quatsch, weil diese Fenster dann zu diesem Thread gehören.

    Lass es einfach bei einem UI-Thread. Mehree UI-Threads bringen nur Chaos und Deadlocks.



  • also was ich damit eigentlich erreichen wollte ist, bei Bedarf im Parentfenster neue childs zu erstellen und wieder zu entfernen, also wenn man zB gerade eine progressbar braucht erstellt man eine und löscht sie wieder sobald der vorgang abgeschlossen ist...

    das konkrete Problem das ich momentan habe ist, dass ich den thread, sobald er einmal in der Nachrichtenschleife ankam nicht mehr zu erstellen von Fenstern oder für irgendeine andere tätigkeit verwenden kann, egal ob Primärthread oder andere threads, und ich habe keine Ahnung wie ich es schaffe nicht für jedesmal einen neuen thread aufmachen zu müssen.

    auch wenn ich alle fenster im Primärthread erstelle sehe ich keine Möglichkeit mehr, sobald dieser einmal bei der Nachrichtenschleife ankam, neue Childs zu erzeugen, oder geht das trotzdem?

    kann man das überhaupt so realisieren mit dem erstellen von childs nach Bedarf?

    lg,
    andi01.



  • ok, ich habe jetzt eine Notlösung gefunden die funktioniert, auch wenn es wirklich keine schöne Lösung ist!

    wäre toll wenn jemand eine bessere wüsste! 👍

    hier mal die Notlösung, das sollte aber nach Möglichkeit nicht so bleiben 😃 :

    struct Child
    {
    	HWND parent;
    	int x;
    	int y;
    	int breite;
    	int höhe;
    	HMENU hmenu;
    	HINSTANCE hi;
    	string fensterklasse;
    	string fenstername;
    	DWORD style;
    };
    
    vector<Child> zu_erstellen;
    
    void child_einfügen(HWND parentfenster, int x_oben_links, int y_oben_links, int b, int h, HMENU hmen, HINSTANCE hinstance, string fensterklasse, string fenstername, DWORD windowstyle)
    {
    	Child c;
    
    	c.parent=parentfenster;
    	c.x=x_oben_links;
    	c.y=y_oben_links;
    	c.breite=b;
    	c.höhe=h;
    	c.hmenu=hmen;
    	c.hi=hinstance;
    
    	c.fensterklasse=fensterklasse;
    
    	c.fenstername=fenstername;
    
    	c.style=windowstyle;
    
    	zu_erstellen.push_back(c);
    }
    
    HWND child_erstellen(Child c)
    {
    	HWND hwnd= CreateWindowA(c.fensterklasse.c_str(), c.fenstername.c_str(), c.style, c.x, c.y, c.breite, c.höhe, c.parent, c.hmenu, c.hi, 0);
    
    	return hwnd;
    }
    
    vector<HWND> childs;
    
    void alle_erstellen()
    {
    	for(int a=0;a<zu_erstellen.size();a++)
    	{
    		childs.push_back(child_erstellen(zu_erstellen[a]));
    	}
    
    	zu_erstellen.clear();
    }
    
    DWORD WINAPI dateifenster(LPVOID IpParameter)
    {
    	WNDCLASS wc;
    
    	wc.cbClsExtra=0;
    	wc.cbWndExtra=0;
    	wc.hbrBackground=(HBRUSH) GetStockObject(WHITE_BRUSH);
    	wc.hCursor=LoadCursor(hi3, IDC_ARROW);
    	wc.hIcon=LoadIcon(hi3, IDI_WINLOGO);
    	wc.hInstance=hi3;
    	wc.lpfnWndProc=WndProc3;
    	wc.lpszClassName=TEXT("Dateifenster");
    	wc.lpszMenuName=0;
    	wc.style=CS_HREDRAW|CS_VREDRAW;
    
    	RegisterClass(&wc);
    
    	parent=CreateWindow(TEXT("Dateifenster"), TEXT("Dateimanager"), WS_OVERLAPPEDWINDOW, 100, 100, 600, 600, 0, 0, hi3, 0);
    	UpdateWindow(parent);
    	ShowWindow(parent, SW_NORMAL);
    
    	MSG msg;
    	while(GetMessage(&msg, 0, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    
    		if(zu_erstellen.size()>0)
    		{
    			alle_erstellen();
    		}
    	}
    
    	return msg.wParam;
    }
    
    int main() 
    { 
    	DWORD id;
    	HANDLE h=CreateThread(0, 0, dateifenster, 0, 0, &id);
    
    //Simulation eines bei Bedarf eingefügten Childs:
    
    	Sleep(3000);
    	child_einfügen(parent, 200, 200, 100, 40, (HMENU) 5, hi3, "BUTTON", "testbutton2", BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE);
    
    	WaitForSingleObject(h, INFINITE);
    
    }
    

    das wird aber hoffentlich nicht die dauerhafte lösung, es kann ja nicht sein dass man sowas in die Nachrichtenschleife packen muss, da gehört es nun wirklich nicht hin...
    allerdings sehe ich keine ander Lösung momentan weil ich den thread sobald er in der Nachrichtenschleife ist nicht mehr "verwenden" kann zum Fenstererstellen. trtotzdem hoffe ich dass jemand eine bessere Idee hat.

    lg,
    andi01.



  • IMHO ist das keine Notlösung,..
    sondern ist prinzipiell gutes dünken...

    Denn was wäre die alternative:
    n static pointed array in der (Main)WinProc die auf bedarf die fenster erstellt,....

    Und das ist unübersichtlich (Habe das blöderweise in einem Project so gemacht, mit der folge von xKLOC quasi unübersichtlicher Code,...)

    Ich würde den Globalen Vektor aber noch threadsafe machen, sonst könnte der zugriff auf diesem nachher laufzeitfehler verursachen,....

    Grüße ..
    ----------------------------------------------------------------------------
    Edit:
    Ne kleine alternative evtl.:

    while(sgn)
    {
     if(etwas_zu_erstellen)
      erstelle();
    
     if(PeekMessage(&msg,..))
     {
    
     }
      else
     {
       Sleep(12);
     };
    }
    


  • Wenn man irgendwelche langwierigen Abläufe im Programm hat, dann erstellt man einen Threat der läuft parallel zum Hauptthreat. Der Hauptthreat ist für die Bedienung des Hauptfensters und seiner Elemente verantwortlich. Im Hauptthreat kann doch auch die Progressbar erzeugt werden, der Workingthreat setzt dann nur noch den Balken der Progressbar.
    Wo ist das Problem?


  • Mod

    Vermeide es Child Windows in anderen Threads zu erzeugen.

    Ich würde grundsätzlich immer zu folgendem Vorgehen raten:
    1. GUI Thread: In dem Du auch on the fly Fenster erzeugen kannst.
    2. Workerthread der die Arbeitmacht. Der sendet/postet Nachrichten oder benutzt andere IPC (Events, Queues etc.) für den Datenaustausch.
    3. Der UI Thread ist für die Nachrichtenbheandlung zuständig.

    Mehrere UI Threads sind möglich aber ein nicht ganz ungefährlicher Konstrukt. Alleine was Modale Fenster betrifft etc... Es geht aber man muss ganz genau wissen was man tut.

    Deine Methode ist nicht ok weil:
    - Sobald Dein Child an das Parent etwas sendet (und das sind nicht wenige Nachrichten), kann dieses locker blockieren, weil Du im Main-Thread ein WaitForSingleObject machst.
    - Zudem werden alle anderen Fenster im Main Thread durch den WaitForSngleObject unresponsive. D.h. es werden keine Nachrichten mehr für diese Fenster empfangen.
    - Du räumst nicht auf. Du verlässt Dich darauf, dass der terminierende Thread alle Fenster killt.



  • Martin Richter schrieb:

    Deine Methode ist nicht ok weil:
    - Sobald Dein Child an das Parent etwas sendet (und das sind nicht wenige Nachrichten), kann dieses locker blockieren, weil Du im Main-Thread ein WaitForSingleObject machst.
    - Zudem werden alle anderen Fenster im Main Thread durch den WaitForSngleObject unresponsive. D.h. es werden keine Nachrichten mehr für diese Fenster empfangen.
    - Du räumst nicht auf. Du verlässt Dich darauf, dass der terminierende Thread alle Fenster killt.

    Hi Martin,
    wenn bekannt ist, das in jedem thread in welches ein fenster erstellt wird, auch ein message handler sein muss, dann sollte dies doch kein problem darstellen...?!

    Sein Message Handler is innerhalb des seperaten Thread in welchem das Hauptfenster und die Childwindows erstellt werden.

    Daher sehe ich nicht die von Dir angesprochenen Probleme.

    Grüße



  • Martin Richter schrieb:

    Ich würde grundsätzlich immer zu folgendem Vorgehen raten:
    1. GUI Thread: In dem Du auch on the fly Fenster erzeugen kannst.
    2. Workerthread der die Arbeitmacht. Der sendet/postet Nachrichten oder benutzt andere IPC (Events, Queues etc.) für den Datenaustausch.
    3. Der UI Thread ist für die Nachrichtenbheandlung zuständig.

    2. ist klar, das mache ich sowieso.

    aber zu 1. und 3.: das heißt, ich soll einen Thread machen in dem nur die Nachrichtenschleife läuft und einen zweiten der bei Bedarf Fenster erstellt?

    ich habe das mal so versucht, um nicht schon wieder die ganzen unveränderten Funktionen zu posten hier nur mal ein Schema:

    DWORD WINAPI Nachrichtenthread(LPVOID IpParameter)
    {
        //hier Nachrichtenschleife
    }
    
    DWORD WINAPI Workthread(LPVOID IpParameter)
    {
        //hier Arbeit
    }
    
    DWORD WINAPI Fensterthread(LPVOID IpParameter)
    {
        //hier Parent erstellen
        //hier fenster bei bedarf erstellen
        //hier keine Nachrichtenschleife
    }
    

    das Problem dabei ist, dass schon das Parent nicht auf Nachrichten reagiert weil es zu einem anderen thread gehört 😞 .

    so ein Schema wäre bereits völlig ausreichend um den Ansatz zu verstehen, den Code für Nachrichtenschleifen und Fenster erstellen kann ich ja selbst einfügen, es geht nur darum wohin damit 😉

    wäre schön wenn jemand mal so ein Schmea postet damit ich verstehe was wohin muss, den Code füge ich dann selbst schon ein 🙂

    lg,
    andi01.



  • Hi,
    Der Messagequeue ist threadspezifisch.
    Du bekommst nur Nachrichten aus diesen Queue für ein Window das Du auch innerhalb des Threads erstellt hast!

    Anstatt IPC würde ich lieber ITC's nehmen, ein zwo signale und ein locked speicherbereich reichen da völlig, um dies ThreadSafe zu machen.

    Das mit den WorkerThreads ist dann halt so eine sache,..
    Du musst aufpassen dass am ende auch das Handle zu diesem geschlossen wird, damit diese resourcen dann auch wieder frei gegeben werden.

    Sollteste also aus der WndProc oder ähnlich heraus den thread erzeugen, solltest Du darauf achten das:
    a) wirklich nur genau einer, oder halt ganeu die geforderte anzahl, an workerthreads erzeugt werden.
    b) das Du nach dem ende des threads auch ordentlich aufräumst...

    Bei mir sieht dies ähnlich deinem ansatz von weiter oben:

    DWORD WINAPI namespace::class_or_struct_or_extern::threadproc(LPVOID lpParam)
    {
    
    this->tssSignal._set_sgn(true); //signal das der thread gestartet ist
    
    while(!this->tssSignal._get_done())
    {
      //some windows to create?
      if(this->__serialize())
      {
        if(this->_winVec.size()>0)
        {
          winVec::iterator itCount   = this->_winVec.begin();
          winVec::iterator itEnd     = this->_winVec.end();
          for(;itCount<itEnd;itCount++)
          {
             (*itCount)->_CreateWindow();
          };
         this->_winVec.clear();
        };
       this->__unserialize();
       }
    
      //und nun das message handling
      MSG msg={0};
      if(PeekMessage(&msg,0,0,0,PM_REMOVE))
     {
        if(msg.message==WM_QUIT)
        {
           this->tssSignal._set_done(true);
        }
       else
       {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
        }
     }
     else
     {
      Sleep(12);
      };
    
    };
    
    return 0;
    };
    

    greetz



  • gut, so funktionert es 🙂

    danke an alle!

    lg,
    andi01.


  • Mod

    Ich verstehe immer noch nicht warum und wieso ihr Fenster in einem anderen Thread erzeugen wollt und müsst! 😕



  • ich kann das Erstellen der Fenster ja in den Primärthread verlegen 😉

    es ging mir nur darum wie ich es mit einem Thread hinbekomme on the fly Fenster zu erstellen, aber das hat sich ja geklärt.

    und der Code ändert sich nicht wirklich, er steht eben in main() statt in einem anderen Thread, aber jetzt weiß ich endlich wie man on the fly Fenster erzeugt 🙂

    nochmal danke an alle!

    lg,
    andi01.


Anmelden zum Antworten