(gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
joggel

(gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von joggel »

Hallo,

ich habe ein eigenartiges Phänomen:
Ich möchte bei einem bestimmten Ereignis dem Nutzer ein visuelles Signal ausgeben. In form eines Hakens welcher für 2 sekunden auf dem Bildschirm erscheint.
Ich zeichne dazu ein Fenster ohne Rahmen und zeichne dann in dem Fenster ein Bitmap...alles mit WinApi.
Auf meinem Entwicklungsrechner funktioniert das gut und IMMER.
Auf dem Rechner auf den es aber ankommt, ist übrigens ein Panel mit Windows7 CE, funktioniert das nicht immer. Oft kommt da nur ein weißes Rechteck mit den Ausmaßen des Fensters in dem das BMP gezeichnet werden soll.

An was kann das liegen?
Was gibt es für Möglichkeiten für dieses Verhalten?
Zuletzt geändert von joggel am 09.12.2016, 13:28, insgesamt 1-mal geändert.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von Krishty »

Hast du auf dem Entwicklungsrechner Compositing eingeschaltet (Aero)? Falls ja, probier dort man das Classic Theme aus und schau, ob der Fehler reproduziert.

Meine erste Vermutung wäre, dass WM_ERASEBKGND durchdreht. Behandle das mal bitte durch return TRUE; und stell sicher, dass der Brush deines Fensters auf NULL gestellt ist (stellt man durch die Fensterklasse ein). Dann löscht Windows nicht mehr den Fensterhintergrund für dich.

Falls das nichts bringt, poste bitte Quelltext (das Problem ist zu verschwommen für Ferndiagnose).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
joggel

Re: [WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von joggel »

Also auf diesem Panel ist auch Aero aktiviert....ich glaube also nicht, dass es damit etwas zu tun hat.

Ich habe jetzt die Änderung vorgenommen, die du vorgeschlagen hast. Ich werde es mal probieren...muß halt dazu an dieses Panel.
Aber Code kann ich trotzdem mal posten:

Das Erstellen des Fensters:

Code: Alles auswählen

void MessageBox_Done(const long& timeOfShowing)
{
	std::wostringstream ws;
	ws << messageBoxCounter;
	std::wstring className(L"MessageBox_Done_");
	className += ws.str();

	WNDCLASSW wcx;
	wcx.cbClsExtra=0;
        wcx.cbWndExtra=0;
        wcx.hCursor=LoadCursor(GetModuleHandle(NULL),IDC_ARROW);
        wcx.hIcon=LoadIcon(GetModuleHandle(NULL),NULL);
        wcx.hInstance=GetModuleHandle(NULL);
        wcx.lpfnWndProc=WndProc_MessageBox_Done;
        wcx.lpszClassName=className.c_str(); //_T("MessageBox_Done");
        wcx.lpszMenuName=NULL;
        wcx.style=CS_HREDRAW|CS_VREDRAW;
	wcx.hbrBackground=NULL; //CreateSolidBrush(RGB(95, 95, 95));
 
	if(!RegisterClassW(&wcx))
        exit(-1);
 
	HWND hWnd=CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_TOPMOST ,className.c_str(),className.c_str(),WS_POPUPWINDOW,900,500,100,100,0,0,GetModuleHandle(NULL),0);

	messageBoxCounter++;

        if(!hWnd)
            exit(-1);
 
	ShowWindow(hWnd, SW_SHOW);
        UpdateWindow(hWnd);

	long long start = milliseconds_now();
	while(milliseconds_now()-start < timeOfShowing)
	{
		//InvalidateRect(hWnd, NULL, NULL);
	};

	SendMessage(hWnd, WM_CLOSE, (WPARAM)hWnd, 0);

	MSG msg;
        while(GetMessage(&msg,0,0,0))
	{
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
}
Die Callback-Procedur:

Code: Alles auswählen

HBITMAP hBitmapDone = NULL;

LRESULT CALLBACK WndProc_MessageBox_Done(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
	switch(msg)
	{
	case WM_CREATE:
		{
    		//hBitmapDone = SHLoadDIBitmap(L"\\InternalStorage\\debug\\haken.bmp");
    		if(hBitmapDone==NULL)
			hBitmapDone = (HBITMAP) LoadImage( NULL, L"C:\\Icons\\haken.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
			
		if(hBitmapDone==NULL)
			exit(1);
		}
    	break;
       case WM_DESTROY:
	{
		DeleteObject(hBitmapDone);
		hBitmapDone = NULL;
               PostQuitMessage(0);
        }
	break;

	case WM_ERASEBKGND:
		{
			return TRUE;
		}
 
    case WM_PAINT:
		{
		PAINTSTRUCT 	ps;
    		HDC 			hdc(NULL);
    		BITMAP 		bitmap;
    		HDC 			hdcMem;
		HGDIOBJ 		oldBitmap;

    		hdc = BeginPaint(hWnd, &ps);
		if(hdc==NULL)
			exit(1);

    		hdcMem = CreateCompatibleDC(hdc);
		oldBitmap = SelectObject(hdcMem, hBitmapDone);

		GetObject(hBitmapDone, sizeof(bitmap), &bitmap);
		//BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
		StretchBlt(hdc, 0, 0, 97, 97, hdcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);

		SelectObject(hdcMem, oldBitmap);
		DeleteDC(hdcMem);

    		EndPaint(hWnd, &ps);        
	}
	return 0;
    }
    return DefWindowProc(hWnd,msg,wParam,lParam);
}
 
Deine vorgeschlagenen Änderungen sind schon drinnen.
(Irgendwie ist der Code hier auch etwas seltsam formatiert....naja...sorry)
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: [WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von gdsWizard »

Muss ja nicht unbedingt am Zeichnen der Bitmap liegen. Probier doch mal einen Brush im Fenster zu installieren. Wenn es das EraseBkground ist dann müsste das ja zu sehen sein. Kannst ja auch mal was per Hand in deine Bitmap zeichnen und dann blitten, oder direkt etwas auf deinen PaintDC zeichnen. Dann kannst du es schon mal einkreisen.
joggel

Re: [WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von joggel »

Moin,

ja, ich kam gestern noch nicht dazu das zu testen. Ich hoffe heute kann ich mal daran.

gdsWizard:
Ja, ich glaube auch irgendwie nicht das es explizit am Zeichnen des Bitmaps liegt. Ich "wette" das Selbe passiert auch wenn ich direkt in meinen PaintDC zeichne...irgendwie ist der verhunzt oder so...

Kleine Zusatz info:
Es läuft eine Vollbild-Anwendung, und vor dieser Anwendung möchte ich meinen Haken ausgeben.
Ich werde heute mal testen, ob das auch passiert, wenn diese Anwendung geschlossen ist, und nur der Desktop zu sehen ist.
Ja ich weiß, mit dieser Info hätte ich mal eher kommen sollen :/
joggel

Re: [WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von joggel »

So...getestet.
Leider ohne Erfolg :(

Ich gebe mal etwas Hintergrundinformation.
Es gibt eine SPS und ein großes Panel, welche über LAN miteinander verbunden sind.
Die SPS übernimmt die Steuerung der Anlage, und das Darstellen der GUI.
Das große Panel übernimmt nur die Darstellung des GUIs. Quais ist das große Panel die Fernsteuerung für die SPS.
Wenn auf der SPS ein bestimmtes Programm durchgeführt wird, sendet dieses Programm zum Schluss ein Signal an das Panel über LAN.
Auf dem Panel läuft ein Netzwerk-Sniffer im Hintergrund, und reagiert auf bestimmt Signale die da ankommen.
Also:
Auf dem Panel läuft die GUI als Vollbildanwendung, und wenn ein Signal von der SPS kommt, soll dann entweder ein Haken oder Kreuz als kleines Fenster aufpoppen.

Und jetzt kommt das Eigenartige:
Bediene ich an der SPS das GUI, starte somit dieses Programm welches das Signal sendet, wird der Haken korrekt aud dem großen Panel darchgestellt.
Starte ich jedoch über das Panel (nur GUI) das Programm auf der SPS, wirdf auf dem Panel dann nur ein weißes Rechteck dargestellt :?: :?: :roll:

Es scheint also irgend etwas mit der Touchfunktion zu tun zu haben....ich bin da echt ratlos....
joggel

Re: (gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von joggel »

Sagt mir doch gleich, dass das Anzeigen in einen separat Thread laufen muß.
Jetzt funktioniert es nämlich...
--------------------------------------
Keine Ahnung was das soll.
Aber jedenfalls geht es jetzt^^
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: (gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von Krishty »

wat

Ein Thread sollte ncihts ändern und normalerweise muss alles Rendering im UI-Thread (der auch das Fenster erzeugt hat) geschehen (weil der die Input Queue hat), daher kommt mir das jetzt doppelt merkwürdig vor.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
joggel

Re: (gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von joggel »

Ja...ich finde es auch seltsam. Aber gut zu wissen, dass das Rendern normalerweise im UI-Thread sein muss.
Ich lasse es jetzt erstmal so, weil es funktioniert....
Benutzeravatar
FlorianB82
Beiträge: 70
Registriert: 18.11.2010, 05:08
Wohnort: Darmstadt
Kontaktdaten:

Re: (gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von FlorianB82 »

Dass ein zusätzlicher UI-Thread hier Abhilfe schafft, würde ich eher als Zufall denn als nachhaltige Lösung ansehen.

Bezüglich des Problems habe ich aber folgende Vermutung: In Deiner MessageBox-Funktion blockierst Du ja nach Erstellung des Fensters die weitere Ausführung, bis die gewünschte Anzeigedauer der Box verstrichen ist. Erst dann startest Du die Message-Loop, arbeitest alle bis dahin aufgelaufenen Nachrichten ab, und schließt das Fenster. Das wird nicht zuverlässig funktionieren, da Du so das Fenster effektiv ohne Messsage-Loop betreibst.

Windows sendet auf zwei Wegen Nachrichten an ein Fenster (genauer: an dessen WndProc): Indirekt über die mit dem Fenster assozierte Message-Queue (sogenannte queued messages), und direkt per Aufruf der WndProc (sogenannte non-queued messages). Ob eine Nachricht queued oder non-queued ist, hängt zunächst vom Typ der Nachricht ab. Details hierzu findest Du unter About Messages and Message Queues: Message Routing. Siehe auch SendMessage vs. PostMessage und PostThreadMessage.

In Deinem Fall erhält Dein Fenster also nur die direkt gesendeten Nachrichten, da Du keine Nachrichten aus der Queue holst und sie an das Fenster dispatchst. Unglücklicherweise gehört WM_PAINT aber zu den queued messages, so dass Dein Fenster diese Nachricht eigentlich nie erhalten sollte und folglicherweise nichts zeichnet. Ich vermute aber stark, dass Windows unter gewissen Umständen WM_PAINT doch auch direkt zustellt - aber das wäre dann nicht näher spezifiziertes Verhalten, das vermutlich je nach Umständen oder OS-Version wild variiert. Windows hält ja beispielsweise keine Anwendung davon ab, eine queued message direkt zu versenden oder eine non-queued message in eine queue zu posten - da scheint es also einen gewissen Graubereich zu geben.

Dafür spricht auch das von dir beobachtete Verhalten, dass bei Anlegen einer zweiten Message-Queue (für jeden Thread, der mindestens ein Fenster erzeugt, erzeugt Windows eine Message-Queue) die Paint-Nachrichten plötzlich ankommen: Offensichtlich triggert das aus welchen Gründen auch immer den Direkt-Versand.

TL;DR: Ein Fenster ohne Message-Loop funktioniert nicht richtig. When in Rome, do It like the Romans: Erzeuge das erst Fenster, dann einen One-Shot Timer für die gewünschte Anzeigedauer, und bearbeite dann die Message-Loop in der zugehörigen Hauptschleife. Sobald Du die WM_TIMER-Nachricht erhältst, schließe das Fenster.

Übrigens: Busy-Loops wie der von Dir verwendete while(!timeElapsed){}; sind ganz arg bäh! Erstens verbrennen sie 100% der dem Thread maximal zur Verfügung stehenden Zeit (schau mal in den Task-Manager!). Zweitens passen sie so gar nicht zum ereignisbasierten Design, wo die Ereignis-Handler zeitnah die Kontrolle an den Main-Loop zurückgeben sollten, um die anderen Ereignisse nicht zu blockieren.
Benutzeravatar
FlorianB82
Beiträge: 70
Registriert: 18.11.2010, 05:08
Wohnort: Darmstadt
Kontaktdaten:

Re: (gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von FlorianB82 »

joggel hat geschrieben:Ja...ich finde es auch seltsam. [...] Ich lasse es jetzt erstmal so, weil es funktioniert....
No Offense gegen Dich persönlich (!), aber:

Genau das ist ein sehr weiterverbreitetes Argument mit dem Leute halbgare Lösungen, die sie selber nicht verstanden haben, als erledigt abhaken. Das Problem dabei: Früher oder später werden diese Halb-Lösungen zu handfesten Problem, die dann oftmals andere Leute ausbaden dürfen. Sprich: Weil der Original-Author zu faul war, das Problem vollständig zu verstehen, haben andere Leute später meistens unverhältnismäßig mehr Aufwand. So etwas habe ich leider oft genug in Produktivsoftware gesehen, und habe da schon eine gewisse Vorschädigung.

Anders ausgedrückt: Wenn ich über Unstimmigkeiten stolpere, gehe ich davon aus, dass ich noch etwas falsch mache oder nicht verstanden habe, und investigiere das Problem gründlich. Oftmals lerne ich dadurch etwas Neues dazu. Wenn Du also die Zeit hast, das zu tun: Nimm sie Dir :).
Zuletzt geändert von FlorianB82 am 12.12.2016, 14:17, insgesamt 1-mal geändert.
joggel

Re: (gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von joggel »

Wow! Okay... klingt irgendwie sehr logisch.
Ich werde das mal auf WM-Timer umschreiben, und nicht das alles mit einer While-Schleife blockieren...

Und ja, verstanden habe ich das mit der WinApi nicht zu 100%, das stimmt. Aber du hast da ja etwas Licht ins dunkle gebracht....
Benutzeravatar
FlorianB82
Beiträge: 70
Registriert: 18.11.2010, 05:08
Wohnort: Darmstadt
Kontaktdaten:

Re: (gelöst)[WinApi]: Fehlschlagen des Zeichnens eines BMP

Beitrag von FlorianB82 »

Keine Ursache. Und nebenbei: Die WinAPI ist aber auch schon ein ganz schöner Brocken! Ich habe auch ne Weile gebraucht, bis ich diese vielen kleinen Details, die man für eine solide Implementierung dann einfach wissen muss, im Überblick hatte. Ein wenig Lesestoff habe ich dir ja oben verlinkt - da wird dir sicherlich Einiges klarer werden.

Und wenn man einfach mal so zwischendurch ein wenig hinter die Fassaden der WinAPI sehen möchte, kann ich Dir The Old New Thing, den Blog von Raymond Chen, sehr empfehlen. Das ist ein Entwickler von Microsoft, der über diverse Probleme, Lösungen, und Erfahrungen bei und mit der Entwicklung der WinAPI berichtet und nebenbei sehr unterhaltsam aus dem Nähkästchen plaudert.
Antworten