Forum: PC-Programmierung Assertion in wincore.cpp (MFC)


von Michael K. (ampfing)


Lesenswert?

[EDIT]
könnte den Beitrag bitte ein Moderator in die PC-Programmierung 
verschieben, ist definitiv das falsche Forum.
Danke
[/EDIT]

Hallo zusammen,

folgende Ausganssituation:
Ich habe eine DLL, die mit einer CAN-Karte kommunizieren soll (dafür 
gibt es vom Hersteller der Karte eine DLL). Meine DLL hat zunächst nur 
einen Dialog.
Jetzt braucht die CAN-DLL (um mir mitteilen zu können, dass eine 
CAN-Nachricht von der Karte empfangen wurde) ein Fenster-Handle. Um den 
CAN-Empfang jedoch von meinem Dialog unabhängig zu halten, wollte ich 
einen zweiten (unsichtbaren) Dialog erstellen, mit dessen MessageQueue 
ich die Nachrichten von der CAN-DLL empfangen und entsprechend 
verarbeiten kann.
Soweit kein großes Problem.

Der Code des zweiten Dialogs (CCanMessageReceiver) ist bislang recht 
übersichtlich:
1
IMPLEMENT_DYNAMIC(CCanMessageReceiver, CDialog)
2
3
CCanMessageReceiver::CCanMessageReceiver(CWnd* pParent /*=NULL*/)
4
  : CDialog(CCanMessageReceiver::IDD, pParent)
5
{
6
   Create(IDD_CAN_MESSAGE_RECEIVER, pParent);
7
}
8
9
CCanMessageReceiver::~CCanMessageReceiver()
10
{
11
   DestroyWindow();
12
}
13
14
void CCanMessageReceiver::DoDataExchange(CDataExchange* pDX)
15
{
16
  CDialog::DoDataExchange(pDX);
17
}
18
19
BOOL CCanMessageReceiver::OnInitDialog()
20
{
21
   CDialog::OnInitDialog();
22
   return TRUE;
23
}
24
25
BEGIN_MESSAGE_MAP(CCanMessageReceiver, CDialog)
26
END_MESSAGE_MAP()

Das Problem an der Sache ist, dass ich bei dem Aufruf von DestroyWindow 
im Destruktor eine Assertion in der wincore.cpp Zeile 398 bekomme. Wenn 
ich den expliziten Aufruf raus nehme bekomme ich eine TRACE-Nachricht, 
dass die Funktion implizit gerufen wird und es kommt die gleiche 
Assertion.
Wenn ich das richtig verstanden habe ist die Zeile 398 Bestandteil der 
Funktion, die für das Verteilen von Nachrichten zuständig ist (Code s. 
unten). Es schaut also so aus, als ob jemand meinem nicht mehr 
vorhandenen Dialog versucht eine Nachricht zu schicken... Nur wer?
Hier der Code der Funktion, in der die Assertion ausgelöst wird:
1
////////////////////////////////////////////////////////////////////////////
2
// The WndProc for all CWnd's and derived classes
3
4
LRESULT CALLBACK
5
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
6
{
7
  // special message which identifies the window as using AfxWndProc
8
  if (nMsg == WM_QUERYAFXWNDPROC)
9
    return 1;
10
11
  // all other messages route through message map
12
  CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
13
ZEILE 398:  ASSERT(pWnd != NULL);          
14
  ASSERT(pWnd==NULL || pWnd->m_hWnd == hWnd);
15
  if (pWnd == NULL || pWnd->m_hWnd != hWnd)
16
    return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
17
  return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
18
}
Ich habe auch schon versucht dem Dialog vor dem Destuktoraufruf eine 
WM_CLOSE-Nachricht zu schicken, aber die kommt nie an (zumindest wird 
der Breakpoint in der OnClose-Funktion nie angesprungen.

Kann mir jemand weiter helfen, wie ich die Assertion beseitige?

Vielen Dank vorab fürs lesen und viele Grüße

: Verschoben durch User
von Karl H. (kbuchegg)


Lesenswert?

Hmm.
Das ist offenbar ein nicht modaler Dialog.

Kannst du ein vollständiges Minimalbeispiel zusammenstellen und Zippen?

IMHO.
DestroyWindow kommt nicht in den Destruktor, sondern zb in die OnCancel 
Funktion.
Dafür fehlt mir die PostNCDestroy Funktion, die eigentlich nur delete 
this machen machen muss


Für nicht modale Dialoge gibt es einige Artikel im Web, die einem 
zeigen, wie sie zu machen sind. Damit sollte es eigentlich keine 
Probleme geben. Bei MFC Fragen ist auch heute noch CodeProject immer ein 
guter Anlaufpunkt.
http://www.codeproject.com/KB/dialog/gettingmodeless.aspx

von Michael K. (ampfing)


Lesenswert?

Hallo Karl-Heinz,

danke für die Antwort und den Link zu dem Artikel.
Leider gestaltet sich das mit dem Minimalbeispiel etwas schwierig und 
der Artikel beschreibt auch nicht unbedingt mein Szenario...

Deswegen versuche ich das Ganze mal etwas ausführlicher zu beschreiben.
Meine DLL soll eine Simulation in der KEIL Entwicklungsumgebung werden. 
Dafür muss meine DLL eine Funktion exportieren, die von KEIL zu 
bestimmten Zeitpunkten aufgerufen wird. Einer der Zeitpunkte ist, wenn 
ich mein Hauptfenster anzeigen muss. In diesem Fall wird das 
Hauptfenster erzeugt und eben angezeigt.
Gleichzeitig habe ich mehrere statische Objekte (die also angelegt 
werden, sobald die DLL geladen wird). Eins davon ist mein unsichtbarer 
Dialog.
Da die Konstruktion der statischen Objekte vor dem Aufruf von 
CWinApp::InitInstance(); passiert habe ich das Create aus dem 
Konstruktor des unsichbaren Dialogs herausgenommen und in eine extra 
Funktion gepackt, die sicher erst gerufen wird, wenn KEIL meine DLL 
geladen hat und CWinApp::InitInstance() gerufen wurde.

Wenn ich den Debugger von KEIL schließe wird auch meine DLL entladen - 
und damit natürlich die statischen Objekte zerstört.
Wenn ich im Destruktor meines unsichtbaren Dialogs kein DestroyWindow() 
stehen habe bekomme ich folgende TRACE-Nachricht in VisualStudio:
1
Warning: calling DestroyWindow in CDialog::~CDialog --
2
  OnDestroy or PostNcDestroy in derived class will not be called.
3
Second Chance Assertion Failed: File f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp, Line 398
4
uv3.exe hat einen Haltepunkt ausgelöst.
Wenn ich den Aufruf drin habe ist die erste Warning weg, der Rest bleibt 
bestehen.

Die Anmerkung mit dem PostNcDestroy (und dem darin befindlichen delete 
this) bringt mich leider nicht weiter, weil ich den Dialog ja nie mit 
new allokiere - und deswegen auch kein delete machen darf...

Ich hatte auch versucht meinem unsichtbaren Dialog vor der Destruktion 
eine WM_CLOSE-Nachricht zu schicken, aber die kam wie gesagt nicht an 
(auch wenn ich nicht verstehe, warum nicht...).

Wie gesagt, das Ganze als Minimalbeispiel zu gestalten ist schwierig, 
weil Du dann auch den KEIL bräuchtest und dort wiederrum ein Projekt, 
das dann die DLL lädt etc.

Ich hoffe dennoch, dass meine Ausführungen mein Problem verdeutlicht 
haben und mir vielleicht doch geholfen werden kann.?

Viele Grüße

von Karl H. (kbuchegg)


Lesenswert?

Michael Klose schrieb:

> Wie gesagt, das Ganze als Minimalbeispiel zu gestalten ist schwierig,
> weil Du dann auch den KEIL bräuchtest und dort wiederrum ein Projekt,
> das dann die DLL lädt etc.

Kopfkratz.
Ja, das ist in der Tat dann schwierig.

> Gleichzeitig habe ich mehrere statische Objekte (die also
> angelegt werden, sobald die DLL geladen wird). Eins davon ist
> mein unsichtbarer Dialog.

> Da die Konstruktion der statischen Objekte vor dem Aufruf von
> CWinApp::InitInstance();

Das muss ja nicht so sein.
Dein statisches "Objekt" kann ja auch ein Pointer auf den Dialog sein, 
der dann erst in der InitInstance tatsächlich mittels new angelegt wird. 
Auf die Art "verzögerst" du die Dialogerstellung ohne dass der Dialog 
etwas davon wissen muss.
Mann könnte sogar weiter gehen und den Dialog erst dann erzeugen, wenn 
er das erste mal gebraucht wird. Ein Singleton-Pattern also.

> Die Anmerkung mit dem PostNcDestroy (und dem darin befindlichen
> delete this) bringt mich leider nicht weiter, weil ich den Dialog
> ja nie mit new allokiere - und deswegen auch kein delete machen darf...

wmoit sich dieses Problem dann auch in Luft auflöst. Der Dialog wird 
nach den klassischen Methoden für modeless Dialoge programmiert, 
"getrickst" wird lediglich in der DLL-Umgebung. Der Dialog muss davon 
gar nichts wissen.

von Michael K. (ampfing)


Lesenswert?

Hi,

Karl heinz Buchegger schrieb:
> Das muss ja nicht so sein.
> Dein statisches "Objekt" kann ja auch ein Pointer auf den Dialog sein,
> der dann erst in der InitInstance tatsächlich mittels new angelegt wird.
> Auf die Art "verzögerst" du die Dialogerstellung ohne dass der Dialog
> etwas davon wissen muss.
> Mann könnte sogar weiter gehen und den Dialog erst dann erzeugen, wenn
> er das erste mal gebraucht wird. Ein Singleton-Pattern also.

So hatte ich das Anfangs auch. Da war dann der Create-Aufruf im 
Konstruktor und der DestroyWindow-Aufruf im Destruktor. Aber das hat 
eben genau das Problem verursacht, deswegen hatte ich gehofft, dass ich 
es anders gelöst bekomme - was sich ja als Irrweg herausgestellt hat...

Deinen Antworten entnehme ich aber, dass ich zumindest mal keinen 
konzeptionellen Bock geschossen habe, oder? So rein von den Überlegungen 
her sollte das also funktionieren, oder?

Andere Frage: hast Du nen KEIL installiert (gibts als kostenlose Demo)? 
Wenn ja würde ich am Wochenende doch mal versuchen, ob ich das Ganze als 
Minimalbeispiel hinbekomme und würds am Montag hier reinstellen.

Ich verstehe halt nicht so recht, wer meinem Fenster eine Nachricht 
schicken sollte, nachdem es nicht mehr da ist - und vor allem verstehe 
ich ganz und gar nicht, warum es auch keine WM_CLOSE-Nachricht empfängt, 
wenn ihm diese gesendet wird.

Viele Grüße

von Michael K. (ampfing)


Lesenswert?

Hallo zusammen,

das Problem ist gelöst!
Es hatte einen ganz anderen Grund, als ich eigentlich gedacht hatte - 
wie das immer so ist...
Falls jemand mal ein ähnliches Problem hat, hier noch meine Erklärung:

Das Problem war, dass die Funktion DestroyWindow() ZU SPÄT gerufen 
wurde. Da das Objekt statisch angelegt wurde wurde es erst zerstört, als 
die DLL schon entladen war (meine Vermutung). Zu diesem Zeitpunkt war 
wohl schon ein Teil des Dialogs zerstört und dadurch kam die Assertion.
Ich habe jetzt herausgefunden, dass der KEIL-Simulator bekannt gibt, 
wenn er beendet wird. Jetzt habe ich zu implementiert, dass der Dialog 
zu diesem Zeitpunkt benachrichtigt wird und die DestroyWindow()-Funktion 
ruft -> keine Assertion mehr.

Viele Grüße

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.