Fehler in FileDialog-Aufruf

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Hallo, ich habe Probleme einen FileDialog fehlerfrei aufzurufen. Manchmal hängt sich das Programm. Der Fehler tritt nur in der Debug Version auf und das bei GetOpenFileName() und auch bei GetSaveFileName(). Es gehen beim Aufruf auch 104 Byte verloren, einmal 64 Byte und einmal 40 Byte. Irgendwie sehe ich den Fehler nicht.
Es wird mit ASCII - Einstellung unter VS 2017 compiliert.


Hier GetOpenFileName:

Code: Alles auswählen

		OPENFILENAME	oOpenFileName;
		char			_szFile[ 65536 ] = "";
		char			_szDir[ 256 ];


		ZeroMemory(&oOpenFileName, sizeof(oOpenFileName));

		oOpenFileName.lStructSize = sizeof(oOpenFileName);
		oOpenFileName.hwndOwner = pMainFrame->GetHWnd();
		oOpenFileName.lpstrFile = _szFile;
		oOpenFileName.nMaxFile = sizeof(_szFile);
		oOpenFileName.nFilterIndex = 1;
		oOpenFileName.lpstrFileTitle = NULL;
		oOpenFileName.nMaxFileTitle = 0;
		oOpenFileName.lpstrInitialDir = _szDir;
		oOpenFileName.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ENABLESIZING | OFN_EXPLORER | ( bMultiSelect ? OFN_ALLOWMULTISELECT : 0 );

		strcpy_s( _szFile, &strPathFileName[ 0 ] );
		strcpy_s( _szDir, &strPathFileName.GetPath()[0] );

		if (m_strFilter.GetCount())
			oOpenFileName.lpstrFilter=&m_strFilter[0];
		else
			oOpenFileName.lpstrFilter=_T("All Files (*.*)\0*.*\0");


		if (!GetOpenFileName(&oOpenFileName))
			return false;
und hier GetSaveFileName():

Code: Alles auswählen

		OPENFILENAME	oOpenFileName;
		char			_szFile[65536]="";


		ZeroMemory(&oOpenFileName, sizeof(oOpenFileName));

		oOpenFileName.lStructSize = sizeof(oOpenFileName);
		oOpenFileName.hwndOwner = pMainFrame->GetHWnd();
		oOpenFileName.lpstrFile = _szFile;
		oOpenFileName.nMaxFile = sizeof(_szFile);
		oOpenFileName.nFilterIndex = 1;
		oOpenFileName.lpstrFileTitle = NULL;
		oOpenFileName.nMaxFileTitle = 0;
		oOpenFileName.lpstrInitialDir = NULL;
		oOpenFileName.Flags = OFN_PATHMUSTEXIST | OFN_ENABLESIZING;

		strcpy_s(_szFile,&strPathFileName[0]);

		if (m_strFilter.GetCount())
			oOpenFileName.lpstrFilter=&m_strFilter[0];
		else
			oOpenFileName.lpstrFilter=_T("All Files (*.*)\0*.*\0");


		if (!GetSaveFileName(&oOpenFileName))
			return false;
Zuletzt geändert von Krishty am 19.05.2018, 14:15, insgesamt 1-mal geändert.
Grund: Code-Tags auf C++ gesetzt
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Der Fehler steht wahrscheinlich im Zusammenhang mit Visual Leak Detektor. Der ist nur in der Debug - Version drin.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

Hmmm … gut möglich. Dein Code sieht auf den ersten Blick korrekt aus.

Der einzige Unterschied zu meinem eigenen Code ist OFN_EXPLORER | OFN_ALLOWMULTISELECT. Wofür brauchst du OFN_EXPLORER? Bei mir hat das keine Auswirkungen auf den Dialog; die Beschreibung im MSDN klingt aber danach, dass es Race Conditions auslösen könnte (macht den Dialog zu einem Child des Explorers).

Mein Code zur Vollständigkeit:

Code: Alles auswählen

			// Shows a dialog asking the user for a path to load a file from.
			//  • “yes” indicates the user has chosen a path which is written to “path”
			//  • “no” indicates the user having aborted the dialog; “path” is undefined
			// Example for filters:
			//     UTF16("Bitmaps (.BMP)\0*.BMP;.DIB\0Text Files (.TXT)\0*.TXT\0")
			// Applies the default file extension only if the user doesn’t type one.
			bool askUserForOpenPath(
				Window * const  toParentWindowIfModalOrNull,
				UTF16Unit (&    path)[pathLengthLimit],
				UTF16Unit const filters[],
				UTF16Unit const defaultExtensionWithoutDotOrNull[]
			) {
				MUST(nullptr == toParentWindowIfModalOrNull || isSameThread_DEBUG(*toParentWindowIfModalOrNull));

				path[0] = 0; // … or “GetOpenFileNameW()” fails
				OPENFILENAMEW parameters = {
					sizeof parameters,
					toParentWindowIfModalOrNull,
					nullptr,                         // no template
					filters,
					nullptr, 0,                      // no customized filters
					1,                               // select the first filter
					path, UInt4B(CAPACITY_OF(path)),
					nullptr, 0,                      // do not provide the stand-alone file name
					nullptr,                         // default directory
					nullptr,                         // default title bar for the dialog
					// Do not let the user type an invalid path.
					0x1800,                          // “OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST”
					0, 0,                            // do not return a file extension
					defaultExtensionWithoutDotOrNull,// default file extension to apply if the user doesn’t type one
					0, nullptr, nullptr,             // no hooks
					nullptr, 0, 0                    // reserved
				};

				return 0 != GetOpenFileNameW(&parameters);
			}

			// Shows a dialog asking the user to pick a directory (but no folder like “Control Panel”).
			//  • “yes” indicates the user has chosen a path which is written to “path”
			//  • “no” indicates the user having aborted the dialog; “path” is undefined
			bool askUserForDirectoryPath(
				Window * const toParentWindowIfModalOrNull,
				UTF16Unit (&   path)[pathLengthLimit]
			) {
				MUST(nullptr == toParentWindowIfModalOrNull || isSameThread_DEBUG(*toParentWindowIfModalOrNull));

				UTF16Unit buffer[260]; // title of the selected folder; forced but needless
				BROWSEINFOW parameters = {
					toParentWindowIfModalOrNull,
					nullptr,    // start at default path
					buffer,
					nullptr,    // no title
					65,         // “BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS”
					nullptr, 0, // no customization
					0
				};
				if(auto toFolder = SHBrowseForFolderW(&parameters)) {
					auto result = SHGetPathFromIDListW(toFolder, path);
					ILFree(toFolder);
					return 0 != result;
				}
				return no;
			}
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Danke für Deine schnelle Antwort. Der Fehler tritt auch in der GetSaveFileName() - Funktion auf ( glaube ich zumindest ). Ich habe jetzt OFN_EXPLORER bei GetOpenFileName() raus genommen. Leider tritt der Fehler nur zeitweise auf. Beim ersten Testen ging es, aber wie bereites gesagt tritt der Fehler nicht immer auf. Wäre schön wenn es das wäre. Ich muss es aber über längere Zeit ausprobieren.
Wie auch immer das Testen ausfällt - Danke für deine Hilfsbereitschaft. Leider wird auch noch immer Speicher nicht freigegeben. Mit Google hatte ich den Hinweis auf Visual Leak Detektor gefunden. Ich schaue mal ob es inzwischen eine neuere Version vom VLD gibt.

Edit: Der Fehler tritt immer noch auf.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

Klingt mir fast nach einer defekten Shell Extension … hast du in dieser Richtung neulich was gemacht (Thumbnail Handlers oder so)?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Nee, ich habe noch nie was in der Richtung gemacht. Der Fehler ist schon seit mehreren Jahren im Framework, vielleicht schon immer. Ich setze auch meinen Rechner mehrfach im Jahr neu auf um definierte Konfigurationen zu haben. Als Thumbnail Handler habe ich nur VLC Player aktiv. Zumindest ist das der einzige der mir im Moment einfällt.
Es gibt 2 Fehlervarianten. Einmal öffnet das Filedialogfenster nicht und ein anderes Mal öffnet das Fenster, aber beim Zeichnen des Inhalts hängt er sich auf. Das betrifft auch das Zeichnen des Abbruch Buttons ( oder wie der heißt ).
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

… Moment, da ist noch ein Unterschied: Du schreibst einen Dateinamen in die Struktur, sogar wenn du nach einem Pfad fragst, der geöffnet werden soll. Hast du mal versucht, den Dateinamen vorher einfach leer zu lassen? Ich erinnere mich, dass ich da schwer zu findende Fehler hatte (deshalb nullt mein Code auch den ersten Buchstaben des Pfades, bevor GetOpenFileName() aufgerufen wird).

(Ist auch logisch schwer nachvollziehbar – wenn du eh schon einen Dateinamen hast, musst du den User doch nicht noch fragen, oder?)

(Außerdem wird Windows für dich automatisch das zuletzt genutzte Verzeichnis speichern und den Dialog dort beim nächsten Mal wieder starten lassen, wenn du das Verzeichnis leer lässt. Das weißt du vielleicht und möchtest es lieber manuell verwalten. Aber falls nicht, kannst du so wieder Code sparen.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Der Fehler tritt auch auf wenn ich einen Leerstring übergebe in File. Die Übergabe macht Sinn, wenn man einen vorgewählten Dateinamen haben möchte. Der Code ist noch aus meinen Anfängen unter Windows. Aber da ist tatsächlich ein Bug, ich trage nämlich den ganzen Dateinamen mitsamt Directory im File ein. File sollte aber nur der eigentlich Dateiname sein. Dieses Feld wird aber nur im Edit Control angezeigt. Dieser Bug ist auch in Visual Studio 2017 unter "{Dateiname} speichern unter ...". Dieser Bug führt aber nicht zum Absturz sondern nur zu einer unübersichtlichen Bedienung.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

Hat der Application Verifier mal was gesagt?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Nee, der Application Verifier erzeugt nur eine fast leere Log-Datei. Das Programm stürzt auch nicht direkt ab, sondern es bleibt beim Zeichnen des FileDialogs einfach stehen oder es kommt erst gar nicht zum Zeichnen. Der Fehler tritt wie gesagt nur in der Debug Version auf. Release klappt immer.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

Wie sieht denn der Call Stack aus, wenn es stehenbleibt?

Hast du daran gedacht, dass das Elternfenster während des Dialogs weiter zeichnen muss (re-entrancy)? Hast du bedacht, dass die WinAPI Exceptions einfach verschluckt, die während des Zeichnens auftreten, und das Programm dadurch in einen inkonsistenten Zustand gelangen kann?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Ich habe eben 10 Minuten probiert und der Fehler ist nicht aufgetreten. Ich habe aber nix geändert, also muss er noch drin sein. Wenn ich das Programm anhalte, wird beim CallStack nur externer Code ohne CallName angezeigt. Aber das im Callback des Mainframes etwas verkehrt läuft ist plausibel. Ich werde mal sehen ob ich da Messages bekomme.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

Naja, die Namen für Calls im externen Code bekommst du ja, wenn du Debug-Symbole runterlädst, während der Code dort angehalten hat.

Optionen -> Debugging -> Symbols -> Häkchen beim Microsoft Symbol Server -> Download All Symbols -> warten -> danach Häkchen wieder weg und Optionen wieder schließen. So siehst du dann, wo er hängt. Vielleicht ein Hinweis, warum.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Wenn das Programm die GetOpenFileName() Funktion aufruft und diesmal gar nicht erst zeichnet kommt unterer CallStack. Die 2 MemoryBereiche die nicht freigegeben werden treten auch in AllocateHeap auf.
FileAufrufError.png
FileAufrufError.png (11.06 KiB) 4655 mal betrachtet
Unten der Callstack von den beiden Speicherleaks, wie sie von VLD erzeugt werden:
FileAufrufError2.png
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

RtlpAllocateHeap() bedeutet, dass gerade allokiert wird. Der Heap ist multi-threaded und wird durch Critical Sections synchronisiert. Wenn das Programm dabei stehen bleibt, bedeutet das, dass die Critical Section des Heaps kaputt ist (nicht mehr freigegeben wurde).

Das passiert entweder manuell (indem jemand HeapLock() aufruft und HeapUnlock() vergisst), oder wenn ein Thread während einer Speicheroperation abstürzt.

Meine Vermutung:
  • an komplett anderer Stelle ist dein Heap kaputt
  • wenn du GetOpenFileName() aufrufst, löst das viele Allokationen aus (denn so ein Explorer-Dialog braucht halt ’ne Menge Ressourcen quer durchs System)
  • bei einer dieser Allokationen fällt auf, dass der Heap beschädigt ist, und das Programm rauscht in einen Haltepunkt
  • da der Code da aber gerade innerhalb eines Callbacks ist, verschluckt Windows die Exception
  • … und hinterlässt den Heap im gesperrten Zustand
  • … und hinterlässt zwei Memory Leaks
  • … und die nächste Allokation hängt sich auf oder löst eine weitere Exception aus.
Ruf vor GetOpenFileName() mal HeapValidate(NULL) auf und schau, ob es dir was ausspuckt. Danach füg deine Anwendung auch im Application Verifier hinzu. (Erst danach, weil HeapValidate() und Application Verifier inkompatibel sind.) Erweiter rechts „Basics“ und klick rechts auf „Heaps“ und wähl „Properties“. Mach Haken bei Size und Protect. Save. Wenn du das Programm im Debugger startest, muss sehr weit oben im Debug Output Page heap: pid 0xDA0: page heap enabled stehen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Im Moment tritt der Fehler wieder nicht auf. Doch ich habe inzwischen eine Fehlermeldung von ApplicationViewer. Sehe ich das richtig das innerhalb von RenderUserChain() meine GUIApplication destruiert wird ? Im Nichtfehlerfall ist der Heap ok, also der von GetProcessHeap().
FileAufrufError3.png
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

~fxGUIApplication … DestroyWindow … FreeLibrary … ja, das sieht so aus.

Was der Application Verifier dort anmerkt ist, dass die DLL, die freigegeben wird, ein Speicherleck hat. Das passiert bei der einen oder anderen Fremdkomponente (Direct3D macht das gern, bzw. Nvidias Grafiktreiber). Im Output des Debuggers müsstest du xxx.dll unloaded direkt vor dieser Meldung sehen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Im Moment habe ich keine Idee wie ich den Fehler finden soll. Unter ApplicationVerifier scheint es keine entsprechenden Fehler zu geben. Ich habe jetzt die Stackbelastung reduziert in dem szFile von 65536 auf 1024 reduziert habe. Aber es ist unwahrscheinlich das der Stack so knapp ist. Aber auf dem Amiga hatte ich einen ähnlichen Fall. Da war der Stack aber nur 4096 groß ( im Standardfall ).
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

Nein, das dürfte nicht der Fehler sein. Dir steht ein Megabyte zur Verfügung. Du würdest auch ganz andere Fehlermeldungen bekommen.

Lass den Application Verifier von jetzt an standardmäßig eingeschaltet. Früher oder später wird er sich melden.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Ja, ich werde ihn drinne lassen. Aber jetzt erscheinen keine Memory Leaks mehr im VLD, dafür meldet Visual Studi etliche 144 Byte nicht freigegeben. Für heute mache ich den Rechner aus. Nochmals Danke an Krishty für deine Hilfe. Den AppViewer kannte ich nocht nicht. Von Dir kann man immer was lernen. :)
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Hier noch ein MemoryLeak in GetOpenFileName(); Vielleicht liegt es ja doch an VLD

https://stackoverflow.com/questions/807 ... ode-vs2010
FileAufrufError4.png
gdsWizard
Establishment
Beiträge: 237
Registriert: 04.02.2005, 09:12
Benutzertext: www.gamedevstudio.com
Echter Name: Thomas Mittelsdorf
Wohnort: Meiningen
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von gdsWizard »

Eben ist der Fehler unter ApplicationViewer wieder aufgetreten. Ich hatte vor den GetOpenFileName() einen bedingten BreakPoint auf HeapValidate() falls es false liefert. Der Debugger hat nicht angehalten. Das bedeutet das der Heap ok war. ApplicationViewer hat nix in die LogDatei geschrieben. Ich musste ja den DebugMode abbrechen, da der MainThread nicht returned ist.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Fehler in FileDialog-Aufruf

Beitrag von Krishty »

Dann bin ich total ratlos …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten