Child window in neuem Thread empfaengt keine Messages



  • Hallo,

    ich versuch mich zum ersten mal mit nem Childwnd in nem neuen Thread,
    zur Probe hab ich mal ein kleines Testprogramm geschrieben.
    Leider gibt es da ein Problem,
    Messages vom Child zum Parent gehen wunderbar, aber andersrum kommt irgentwie nix an.
    Ich verwende PostThreadMessage(...
    Hier mal der Code:

    LRESULT CALLBACK ChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	int wmId, wmEvent;
    	PAINTSTRUCT ps;
    	HDC hdc;
    	static HFONT font;
    	static HPEN pen;
    	static HWND Button1,Button2;
    
    	switch (message)
    	{
    	case WM_CREATE:
    		font=CreateFont(20,0,0,0,FW_NORMAL,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
    			DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,"Arial");
    		Button1=CreateWindow("BUTTON","Info",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
    			20,240,160,30,hWnd,(HMENU)ID_BUTTON1,hInst,NULL);
    		Button2=CreateWindow("BUTTON","Destroy",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
    			20,20,160,30,hWnd,(HMENU)ID_BUTTON2,hInst,NULL);
    		SendMessage(Button1,WM_SETFONT,(WPARAM)font,TRUE);
    		SendMessage(Button2,WM_SETFONT,(WPARAM)font,TRUE);
    		pen=CreatePen(PS_SOLID,2,RGB(255,255,0));
    		return 0;
    	case WM_COMMAND:
    		wmId    = LOWORD(wParam);
    		wmEvent = HIWORD(wParam);
    		switch (wmId)
    		{
    		case ID_BUTTON1:{	HWND prnt=GetParent(hWnd);
    							SendMessage(prnt,WM_COMMAND,(WPARAM)LOWORD(INSTRUCTION1),0);	}break;
    		case ID_BUTTON2:{	HWND prnt=GetParent(hWnd);
    							SendMessage(prnt,WM_COMMAND,(WPARAM)LOWORD(ID_BUTTON2),0);	
    							DestroyWindow(hWnd);PAINTED=FALSE;								}break;
    		case INSTRUCTION2:	MessageBox(NULL,"geht....?","",MB_OK);							break;
    		case IDM_\1:
    			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
    			break;
    		case IDM_EXIT:
    			DestroyWindow(hWnd);
    			break;
    		default:
    			return DefWindowProc(hWnd, message, wParam, lParam);
    		}
    		break;
    	case WM_PAINT:{	
    			int i,a=20,b=60,c=160;
    			HGDIOBJ original;
    			hdc = BeginPaint(hWnd, &ps);
    original=	SelectObject(hdc,pen);
    		MoveToEx(hdc,a,b,NULL);
    		for(i=0;i<9;i++){
    			LineTo(hdc,a+c,b);Sleep(100);
    			LineTo(hdc,a+c,b+c);Sleep(100);
    			LineTo(hdc,a,b+c);Sleep(100);
    			b=b+10;
    			LineTo(hdc,a,b);Sleep(100);
    			a=a+10;
    			c=c-20;		}
    		SelectObject(hdc,original);
    		EndPaint(hWnd, &ps);
    				  }break;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		break;
    	default:
    		return DefWindowProc(hWnd, message, wParam, lParam);
    	}
    	return 0;
    }
    
    void NewThreadFunc(HWND Parent){
    	t_data threadD;
    
    	threadD=(t_data)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(T_data));
    
    				if(threadD==NULL){
    					MessageBox(NULL,"T_data allocation error","ERROR",MB_OK|MB_ICONERROR);}
    
    	threadD->hWnd=Parent;
    
    	toThread=	CreateThread(NULL,0,SimThreadProc,threadD,0,&ChildWndThreadID);
    
    				if(toThread==NULL){
    					MessageBox(NULL,"Thread Creation Error","ERROR",MB_OK|MB_ICONERROR);}
    
    	CloseHandle(toThread);
    }
    
    DWORD WINAPI SimThreadProc(LPVOID lpParam){
    
    	HWND hWnd,nWnd;
    	t_data OtD;
    	OtD=(t_data)lpParam;
    	hWnd=OtD->hWnd;
    	MSG msg;
    	BOOL B_return;
    
    	nWnd=CreateWindow(CWndClass,CWndClass,WS_CHILD|WS_VISIBLE|WS_DLGFRAME,20,60,205,280,hWnd,NULL,hInst,NULL);
    
    	if(!nWnd)ExitThread(2);
    
    	while (B_return=GetMessage(&msg, NULL, 0,0)!=0)
    	{
    		if (B_return==-1){	ExitThread(1);	}
    		else{
    				TranslateMessage(&msg);
    				DispatchMessage(&msg);	}}
    
    return msg.wParam;}
    

    Main-WndProc

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	int wmId, wmEvent;
    	PAINTSTRUCT ps;
    	HDC hdc;
    	static HFONT font;
    	static HWND ClientArea,Button1,Button2,Editbox;
    
    	switch (message)
    	{
    	case WM_CREATE:
    		font=CreateFont(20,0,0,0,FW_NORMAL,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
    			DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,"Arial");
    		Button1=CreateWindow("BUTTON","Start Paint",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
    			20,20,205,30,hWnd,(HMENU)ID_BUTTON1,hInst,NULL);
    		Button2=CreateWindow("BUTTON","Send Message",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
    			20,400,205,30,hWnd,(HMENU)ID_BUTTON2,hInst,NULL);
    		Editbox=CreateWindow("EDIT","",WS_CHILD|WS_VISIBLE|WS_DLGFRAME|ES_CENTER,
    			20,360,205,28,hWnd,NULL,hInst,NULL);
    		SendMessage(Button1,WM_SETFONT,(WPARAM)font,TRUE);
    		SendMessage(Editbox,WM_SETFONT,(WPARAM)font,TRUE);
    			return 0;
    	case WM_COMMAND:
    		wmId    = LOWORD(wParam);
    		wmEvent = HIWORD(wParam);
    		switch (wmId)
    		{
    		case ID_BUTTON1:	if(!PAINTED){	PAINTED=TRUE;
    											NewThreadFunc(hWnd);
    							SetWindowText(Editbox,"PAINTING....");}
    							break;
    		case ID_BUTTON2:{
    						BOOL RS=PostThreadMessage(ChildWndThreadID,WM_COMMAND,(WPARAM)LOWORD(INSTRUCTION2),0);
    						if(!RS){MessageBox(hWnd,"PostThreadMsg - failed!","",MB_OK);}
    							SetWindowText(Editbox,"EMPTY");
    							PAINTED=FALSE;	}break;
    		case INSTRUCTION1:	SetWindowText(Editbox,"Message Empfangen");break;
    		case IDM_\1:
    			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
    			break;
    		case IDM_EXIT:
    			DestroyWindow(hWnd);
    			break;
    		default:
    			return DefWindowProc(hWnd, message, wParam, lParam);
    		}
    		break;
    	case WM_PAINT:
    		hdc = BeginPaint(hWnd, &ps);
    		EndPaint(hWnd, &ps);
    		break;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		break;
    	default:
    		return DefWindowProc(hWnd, message, wParam, lParam);
    	}
    	return 0;
    }
    

    Wenn ich im Hauptfenster auf Button2 klicke soll übers Childfenster ne Messagebox ausgelöst werden.
    Wo liegt mein Fehler? Oder isses vieleicht kompletter Käse was ich da gemacht hab?
    Wär schön wenn mir jemand helfen kann.
    Viele Dank schonmal für die Antworten
    Grüße



  • Ok, der Code is teilweise ein bisschen sinnlos, ich weiß, aber war ja auch nur zum testen wie Threads funktionieren, das Problem ist aber klar glaub ich,
    kann mir keiner nen Tipp geben....?



  • Mit PostThreadMessage abgesetzte Nachrichten gehen natürlich nicht an irgend ein Fenster. Sollte klar sein nachdem man kein Window-Handle übergeben kann.
    Wieso erwartest du dann dass du über die Window-Proc nen WM_COMMAND reinbekommen würdest?

    Entweder schick' die Nachricht mit PostMessage an das Child-Fenster, oder behandel die Nachricht manuell in der Message-Pump Schleife des Threads.

    Und sei dir im Klaren darüber dass Parent<->Child Beziehungen zwischen Fenstern in unterschiedlichen Threads problematisch werden können, und allgemein als Schlechte Idee (tm) anerkannt sind.



  • Ja, ok, jetzt wirds mir auch klar.
    Steht ja auch im MSDN bei PostThreadMessage, hab ich irgentwie überlesen...

    Das Parent<>Child bei Threads problematisch sein kann weiß ich, aber ich will das dass Childwindow an das Parent gekoppelt ist,
    wenn ich es nicht als Child deklariere geht es immer als Popup auf und das will ich nicht. Gibts da irgenteine Möglichkeit das ohne WS_CHILD hinzubekommen?
    Grüße



  • Mir ist keine andere Möglichkeit bekannt.
    Aber warum muss es denn nen eigenen Thread haben?



  • Also ich brauch das für ein anderes Programm wo ich eine Simulation über WM_PAINT laufen lasse und das dauert ziemlich lange,
    jetzt wollt ich aber nicht das dass Programm so lange hängt, da gibt es noch mehr Childwindows die eventuell nebenbei bearbeitet werden müssen.
    Ich hatte auch schon versucht nur die Simulationsfunktion in einen Thread zu stecken, aber wie gesagt ist es ja problematisch Handles zu übergeben und wenn ich das hdc übergebe hängt sich das Programm auf. Das brauch ich ja aber um zu zeichnen,
    deswegen dachte ich, ich stecke das ganze Window in einen Thread.
    Ich kann mir ja aber mit GetWindowPlacement die Pos vom Hauptfenster holen das neue Wnd passen dazu anordnen, muss ich noch ein wenig basteln....



  • Pack die Simulation in einen eigenen Thread.

    Die Simulation soll dabei nur simulieren, nicht zeichnen.
    Jedes mal wenn ein "Frame" (zeichenbarer Snapshot des simulierten Systems) fertig ist, dann soll die Simulation dieses in eine Queue stellen und das Fenster benachrichtigen dass es was neues zu zeichnen gibt.

    Alternativ, falls das Zeichnen selbst so lange dauert, kann der Simulations-Thread auch gerne zeichnen -- nur halt nicht direkt ins Fenster (*).
    Also die Simulation rendert dann z.B. in eine Memory-Bitmap, Direct3DSurface, Direct2DSurface - irgend sowas. Und wenn eine neue Bitmap fertig ist, dann übergibt sie diese dem Window-Thread.

    Dabei kannst du entweder für jeden Simulationsschritt ein neues Objekt (Simulations-Snapshot bzw. Bitmap) erzeugen, welches du im Fenster Thread dann löscht sobald das nächste Objekt übergeben wird. Oder mit einer Art Double- oder Triple-Bufferig arbeiten, wo der Window-Thread die Objekte die er nicht mehr braucht an Simulations-Thread zurückgibt, der sich dann einfach wiederverwenden kann.

    Das wäre mMn. die saubere Variante. Ich würde hier auf jeden Fall keinen Workaround mit mehreren GUI Threads machen wollen.

    *: Wobei das theoretisch sogar geht, es ist nur pfui. Abstürzen dürfte deswegen aber nichts.



  • Ja, ok, also das mit dem Bitmap zeichnen und dann ins Fenster laden ist ungünstig,
    weil die Simulation fährt ein CNC Programm ab und da muss ich wirklich explizit jeden Verfahrweg des Werkzeuges mitverfolgen können.
    Die erste Methode die du genannt hast wäre da wohl besser geeignet, muss ich mal sehen ob ich das hinbekomme.
    Is ja echt nen guter Tip, würd ich die Simulationsklasse neu schreiben wär das kein Problem, aber das hat mich so viel Zeit gekostet und ist derart komplex das ich keine Lust hab das nochmal komplett neu zu schreiben,
    mal sehen vieleicht kann ich das mit wenig Aufwand umschreiben, einfach würd das aber auch nicht.
    Ich hab auch nochmal ein wenig rumgebastelt, ich hab jetzt das Fenster soweit das es sich genau wie ein ans Hauptfenster gekoppeltes Childwindow verhält, macht alle Bewegungen und Größenveränderungen mit, es gibt nur ein Manko, wenn das Sim-Fenster den Focus bekommt kommt unter dem Hauptfenster die Windows-Startleiste durch,das is blöd, aber wird vieleicht auch hinzubekommen sein.
    Danke für deine Hilfe 🙂



  • wenig Aufwand umschreiben, einfach würd das aber auch nicht

    Verkacktes Design macht immer Aufwand, ob durch Neuschreiben/Designen oder beim Arbeiten damit. Deswegen gibt es ja Muster. In deinem Fall ist Model-View-Controller angebracht. In Computerspielen (auch nur eine Simulation mit Benutzerinteraktion) ist die Darstellung vom Rest entkoppelt.


Log in to reply