PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : VC++/MFC: Device Context wie oft neu anfordern?


Gast
2005-09-11, 23:09:22
Hallo,

Ich arbeite z.Z. an einem Programm, das Funktionsplots in einem x-y-Koordinatenkreuz graphisch ausgibt. Dazu verwende ich eine Klasse CGraph von www.codeguru.com. In dieser Klasse wird sehr häufig (genauer gesagt für jede einzelne Zeichenoperation einer Linie zwischen zwei Punkten des Funktionsgraphen, also für einen vollständigen Graphen mehrere hundert- oder gar tausendmal!) ein neuer Device Context angefordert (natürlich auch jedes Mal wieder freigegeben).

Sofern ich einmal einen Graphen zeichne und den dann unverändert so stehen lasse, gibt's dabei auch keine Probleme. Zeichne ich aber fortwährend einen neuen Graphen, weil sich die darzustellende Funktion laufend verändert (Echtzeitvisualisierung von Meßdaten), mit einer Framerate von etwa 5 fps, gibt es meist nach etwa 1000 Frames einen Programmabsturz, was - wie ich herausgefunden habe - daran liegt, daß für den angeforderten Device Context nur noch NULL zurückgegeben wird.

Kann das daran liegen, daß die Häufigkeit, mit der ein Device Context angefordert wird, schlicht zu hoch ist? In der Zeit bis zum Programmabsturz passiert das immerhin einige hunderttausendmal. Ich hab mir mal meine älteren OpenGL-Programme angeschaut und festgestellt, daß dort für die gesamte Programmlaufzeit immer nur ein einziger Device Context angefordert wird (hab ich von den Tutorials auf http://www.codeworx.org/opengl_tuts.php übernommen). Wäre das vorzuziehen? Spielt es vielleicht eine Rolle, ob das Fenster, dessen Device Context angefordert wird (das Fenster in der die CGraph-Instanz integriert ist), die Eigenschaft CS_OWNDC hat? Unter der Win32-API konnte man die ja beim Initialisieren der Fensterklasse festlegen, aber unter der MFC scheint das nicht mehr möglich zu sein...

Hier mal ein Codeausschnitt aus der CGraph-Klasse, mit dem Anfordern eines Device Context:

void CGraph::DrawLine(int x1, int y1, int x2, int y2)
{
// Device Context vom Parent Fenster anfordern
// m_pWnd ist ein Pointer auf die CWnd-Instanz des Parent-Fensters
CDC *pdc = m_pWnd->GetDC();
// Zeichenoperationen mit pdc...
// m_pWnd->ReleaseDC(pdc);
}

Wäre statt der Zeile CDC *pdc = m_pWnd->GetDC(); vielleicht ClientDC dc(m_pWnd); besser?

Neomi
2005-09-12, 01:48:03
Es fehlt schlicht der Aufruf von ReleaseDC. Entweder du gibst den Context jedesmal wieder frei, oder du merkst ihn dir einmal, forderst ihn nicht immer neu an und gibst ihn am Ende wieder frei.

Coda
2005-09-12, 01:49:49
Windows 95 hat aber soweit ich weiß sehr begrenzte Resourcen was die DCs betrifft.

Gast
2005-09-12, 09:06:06
Es fehlt schlicht der Aufruf von ReleaseDC. nee, tut er nicht. Ich schrieb doch, daß der jedes Mal dabei ist.

Neomi
2005-09-12, 12:11:54
nee, tut er nicht. Ich schrieb doch, daß der jedes Mal dabei ist.

Im Code, den du oben angegeben hast, ist der Aufruf jedenfalls auskommentiert, dann kann er natürlich nicht greifen. Ansonsten paßt das Verhalten auch zur fehlenden Freigabe.

Gast
2005-09-12, 12:18:44
Im Code, den du oben angegeben hast, ist der Aufruf jedenfalls auskommentiert, war ein Tippfehler. Außerdem sollte dann der Fehler etwas früher auftreten und nicht erst nach mehreren hunderttausend GetDC()-Aufrufen.

Trap
2005-09-12, 12:33:29
Wäre statt der Zeile CDC *pdc = m_pWnd->GetDC(); vielleicht ClientDC dc(m_pWnd); besser?
Ja, wäre besser. Stichwort: RAII

Ich glaube allerdings nicht, dass die Änderung das Problem behebt.

Gast
2005-09-12, 18:20:53
ich hab's jetzt mal mit einem permanenten (also einmal angeforderten und dann beibehaltenen) DC versucht. Damit ist das Problem zwar, aber dafür tritt nach einer etwas längeren (etwa zweimal so langen) Zeit ein neues Problem auf: die Farben im Plot stimmen plötzlich nicht mehr (statt hellblauer Linien werden schwarze gezeichnet), und die Zeichenfläche verhält sich so, als würde sie gar nicht mehr zum Parent-Fenster (das dessen DC angefordert wurde) gehören, sondern eher zum Desktop, z.B. ist nach einem Verschieben des Parent-Fenster die Zeichenfläche mehrfach vorhanden, einmal da wo das Fenster vorher war, einmal an der neuen Position, und an diversen Stellen dazwischen auch. Außerdem wird das Parent-Fenster nicht mehr vollständig dargestellt, es fehlen u.a. Teile der Fensterleiste, und ein CStatic-Textfeld aus dem Fenster befindet sich plötzlich in der linken oberen Ecke des Desktop, gänzlich außerhalb des Parent-Fensters.
Irgendwas scheint da mit dem DC nicht zu stimmen...

Gast
2005-09-13, 14:11:07
also wie es aussieht, habe ich die Lösung gefunden:
die CGraph-Klasse hat nicht nur zu oft einen neuen DC angefordert, sie hat auch wie blöd Stifte und Pinsel neu erzeugt (mit CPen::CreatePen() und CBrush::CreateSolidBrush()). Das scheint Windows nicht so gerne zu mögen.
Ich habe jetzt einfach einen Satz permanenter Stifte und Pinsel angelegt, für jede im Graphen vorkommende Farbe einen eigenen, von denen jeder nur ein einziges Mal im Konstruktor der CGraph-Klasse initialisert wird.
Damit scheint das Problem behoben zu sein...

Xmas
2005-09-13, 23:51:46
Hattest du denn für die Pens und Brushes auch Release aufgerufen?

Juerg
2005-09-14, 01:13:41
Hattest du denn für die Pens und Brushes auch Release aufgerufen?Ich bin der Meinung, dass Pens und Brushes zuerst mit dem vorherigen Object ersetzt werden (SelectObject) und dann mit DeleteObject weggeputzt werden?

Gast
2005-09-14, 09:26:24
Hattest du denn für die Pens und Brushes auch Release aufgerufen?das sah ungefähr so aus:

CPen cPen, *pOldPen;

// drawing in first color
cPen.CreatePen(PS_SOLID, 1, crColor1);
pOldPen = dc->SelectObject(&cPen); // dc is pointer to actual DC

// some drawing operations with dc using pen

dc->SelectObject(pOldPen);
cPen.Detach();

// next color
cPen.CreatePen(PS_SOLID, 1, crColor2);
pOldPen = dc->SelectObject(&cPen);

// some drawing operations

dc->SelectObject(pOldPen);
cPen.Detach();

// and so on...

ich ging davon aus, das Detach() so was wie eine Freigabe ist, also CreatePen() aufhebt. Ich hatte auch mal testweise Detach() weggelassen und bekam dann sofort beim zweiten CreatePen-Aufruf eine Fehlermeldung.

Gast
2005-09-14, 09:30:44
falls es interessiert, der vollständige Sourcecode der CGraph-Klasse (in der Originalversion, ohne meine Änderungen) findet sich hier:
http://www.codeguru.com/Cpp/controls/controls/chartingandanaloguecontrols/article.php/c9309

Xmas
2005-09-15, 14:27:58
Ich bin der Meinung, dass Pens und Brushes zuerst mit dem vorherigen Object ersetzt werden (SelectObject) und dann mit DeleteObject weggeputzt werden?
Ja, ich hatte aus irgendeinem Grund .net im Kopf (und meinte da eigentlich Dispose, nicht Release).