Programm crasht - nachdem es geschlossen wurde
-
Hallo Leute,
ich habe da ein Problem mit meinem Programm. Das Hauptfenster soll je nach Bedarf ein oder mehrere freie Fenster (nicht im Clientbereich des Parent) creieren. Da die Fenster vom Aufbau gleich sind, habe ich deren Funktion in eine dll gepackt. Die wird beim Programmstart einmal geladen, und bei Programmende entladen._TCHAR* lpClass=_T("DllKlasse"); HINSTANCE hi; BOOL APIENTRY DllMain(HINSTANCE hInst,DWORD fdwReason,LPVOID lpReserved) { hi=hInst; switch(fdwReason) { case DLL_PROCESS_ATTACH: { //klasse registrieren WNDCLASSEX wcex; wcex.cbSize=sizeof(WNDCLASSEX); wcex.style=CS_HREDRAW|CS_VREDRAW; wcex.lpfnWndProc=(WNDPROC)DllWndProc; wcex.cbClsExtra=0; wcex.cbWndExtra=sizeof(LONG_PTR); wcex.hInstance=hi; wcex.hIcon=NULL; wcex.hCursor=LoadCursor(NULL,IDC_ARROW); wcex.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); wcex.lpszMenuName=NULL; wcex.lpszClassName=lpClass; wcex.hIconSm=NULL; if(!RegisterClassEx(&wcex)) return FALSE; } break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: UnregisterClass(lpClass,hi); //für winnt... break; } return TRUE; }dann habe ich noch die Export-Funktion, die mir die Fenster erstellt:
INTERFACE_API HWND CreateTxWindow(HWND hwParent,LPARAM lParam) { return CreateWindow(lpClass,NULL, WS_OVERLAPPEDWINDOW, 300,300,100,100, hwParent,NULL,NULL,(LPVOID)lParam); }und natürlich deren Fensterfunktion (hier stark zurückgeschnitten)
LRESULT CALLBACK DllWndProc(HWND hdll,UINT message,WPARAM wParam,LPARAM lParam) { switch(message) { case WM_CREATE: { HMENU hm; hm=GetSystemMenu(hdll,FALSE); if(hm) { InsertMenu(hm,SC_CLOSE,MF_STRING|MF_BYCOMMAND,IDM_ANSICHT,_T("Ansicht")); } return 0; } break; case WM_SYSCOMMAND: switch(LOWORD(wParam)) { case IDM_ANSICHT: DialogBoxParam(hi,(LPCTSTR)IDD_DIALOG1,hdll,(DLGPROC)DlgAnsichtProc,0); return 0; break; } break; default: break; } return DefWindowProc(hdll,message,wParam,lParam); }Es soll nur deutlich werden, dass jedes Fenster auch einen Dialog erstellen kann.
Das Hauptprogramm registriert die Klasse beim laden der dll, es erstellt über die Export-Funktion das Fenster, die Kommunikation erfolgt dann über das zurückgegebene Fensterhandle. Wird das Hauptprogramm geschlossen, führt es für jedes noch offene Unterfenster einDestroyWindow(hWnd);durch. Danach wird mit
FreeLibrary(..);die Dll entladen. Das funktioniert auch alles super. Ich hatte dann mal das Haupfenster geschlossen, als ein - von einem Unterfenster erzeugter Dialog - noch geöffnet war.
Das Programm wurde zwar sichtlich geschlossen (auch der Dialog verschwand) aber nach einer sekunde kam ein Fenster "test.exe hat ein Problem erzeugt und muss geschlossen werden".
Ich habe dann auch debuggt (MS VC++ 6), aber nach der Postquittmessage, kam nur noch Assembler.7E419494 mov eax,dword ptr [esp+4] 7E419498 int 2Bh //hier absturz, wenn dlg noch offen war 7E41949A ret 4 //fertig debug, wenn kein dlg offen warWar der Dialog noch auf, kommt beim int-Befehl "Acces-Violation" oder sowas, ohne Dialog endet der Debugger ordnungsgemäss nach ret.
Weiss jemand Rat, wo ich den Fehler suchen muss?
-
Du brauchst soetwas wie reference counter für alle geöffnete Fenstern, und eine DllCanUnloadNow export Funktion:
// DLL int g_FensterCount = 0; void DllHandleMessage(UINT uMsg) { switch (uMsg) { case WM_CREATE: case WM_INITDIALOG: g_FensterCount++; // block FreeLibrary break; case WM_DESTROY: g_FensterCount -= (g_FensterCount>0); } } STDAPI DllCanUnloadNow() // dllexport { return (HRESULT)(g_FensterCount != 0); // 0:S_OK oder 1:S_FALSE } LRESULT CALLBACK DllWndProc(HWND hdll,UINT message,WPARAM wParam,LPARAM lParam) { DllHandleMessage(message); switch() ... } // {modale} dialogs: BOOL CALLBACK DllDialogProc(HWND hdll,UINT message,WPARAM wParam,LPARAM lParam) { DllHandleMessage(message); switch() ... } /////////////////// // exe BOOL UnloadPlugin(HINSTANCE& hPlugin) { BOOL retval = FALSE; typedef HRESULT (*DLLCANUNLOAD)(void); DLLCANUNLOAD pfnCanUnload = GetProcAddress(hPlugin, "DllCanUnloadNow"); if (pfnCanUnload && !pfnCanUnload()) { // you can unload this dll, but dispatch first all pending messages // like WM_NCDESTROY, WM_QUIT MSG msg; while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } retval = FreeLibrary(hPlugin); hPlugin = 0; return retval; } // at least one active window is still open (owned by plugin) return retval; }
-
Besser: InterlockedIncrement/Decrement/CompareExchange
-
Erst einmal vielen Dank für eure Antworten. War am WE weg, und konnte erst heute weitermachen.
@sapero
Deinen Ansatz verstehe ich im grossen und ganzen. Es soll verhindert werden, dass FreeLibrary() aufgerufen wird, wenn noch irgendwelche Speicher durch die DlgBox allociert sind. Ich verstehe aber nicht, warum ich diesen Prozess machen muss, nur weil das Fenster aus einer dll heraus erstellt wurde.
Ich habe das Testprogramm so umgeschrieben, dass das Hauptfenster ein Unterfenster creiert, und von diesem der Dialog gestartet wird. Wenn ich jetzt in der WndProc() des Hauptfensters ein DestroyWindow() für das Unterfenster ausführe, wird der Dialog und das Unterfenster anstandslos geschlossen. DestroyWindow() gibt TRUE zurück. Wird jetzt das Hauptfenster geschlossen, wird das Programm ohne Fehler beendet.@Jochen
Die von Dir genannten Befehle hab ich mir angesehen, verstehe aber nicht wie ich sie verwenden sollte?
-
ich nochmal,
Ich habe jetzt herausbekommen, dass es irgendwie mit der HINSTANCE (Hmodule) zu tun haben muss. Mein Hauptfenster bekommt ja eine über WinMain(), die DLL bekommt eine andere, obwohl sie im selben Adressraum liegt. Ich muss aber letztere für das Unterfenster verwenden, weil in der DLL die Resourcen für den Dialog eingelagert sind. Und dieser lässt sich nur anzeigen, wenn die richtige Hinstance verwendet wird.
Wenn das Hauptfenster nun ein DestroyWindow() an das Unterfenster sendet, und dessen Dialog noch geöffnet ist, wird zwar das Unterfenster und der Dialog weggeräumt, aber beim Anschliessenden aufräumen des Hauptfensters kommte es zum Absturz. Muss die Instanz des Unterfensters irgendwie noch entfernt werden, bevor ich das Programm beende?Weiss jemand einen Rat, wie das verhindert werden kann?
-
1. Ohne CallStacj nützen Deine Infos nichts.
2. HINSTANCE hat damit nichtszu tun. Wie Du selber erkannt hast dient diese nur zum ermitteln der Ressourcen und für die Klassenregistrierng.Nochmal zu Deiner Frage warum Du DLL vor em Entalden schützen musst.
Zu einem Fenstergehört die Fensterprozedur. Diese Code Adresse ist in den Fensterdaten verankert. Wenn Du die DLL entlädst, die die Fensterprozedur enthält, dann wird das nächste mal Dein System einfach crashen, wenn eine Nachrichtan solch ein Fenster ausgeliefert wird.Beim Beenden Crashed Deine Anwendung wahrscheinlich wieder, weil evtl. DLL entalden wird, bevor alle Fenster die diese DLL behandelt zerstört wurden.
-
sapero schrieb:
Du brauchst soetwas wie reference counter für alle geöffnete Fenster, und eine DllCanUnloadNow export Funktion: ...
Ein solcher 'reference counter' ist so nicht möglich, den führt Windows allein! Man bräuchte dafür 'shared memory', da jede Instanz der DLL einen eigenen von den anderen Instanzen unabhängigen Datenbereich erhält. Erscheint mir aber nicht notwendig, wenn DllMain beim Ausstieg alles angeforderte sauber wieder freigibt. Falls wegen eines in der DLL geöffneten aber nicht geschlossenen Dialoges beim Schliessen der Anwendung das geschilderte Problem bleibt, empfiehlt sich in der Anwendung bei WM_DESTROY der Aufruf einer DLL-Funktion zum Schliessen des Dialoges. Das sollte so reichen, habe es aber nicht verifiziert. Das geschilderte Problem steckt wohl allein in der DLL.