Zugriffsverletzung bei Dateizugriff



  • Hallo Leute,

    habe 2 Prozesse am laufen, welche teilweise gleichzeitig auf die selben Dateien zugreifen.
    Dabei kommt es natürlich des Öfteren zu Zugriffsverletzungen.
    Nun würde ich gerne wissen wie ich dieses verhindern kann.
    Per MSDN und Google habe ich leider nicht das passende gefunden.
    Gehe anscheinend falsch an die Sache ran.

    Bisher habe ich es so versucht:

    CFileStatus status9;
    								while(CFile::GetStatus(L"\\BertDaten\\EXPORT\\Queue\\" + filename, status9) == false)
    								{
    									Sleep(200);
    								}
    

    Solange kein Status zu dem Objekt zu kriegen ist soll das System "Sleepen".
    Nach der Schleife habe ich dann immer direkt den Aufruf der Datei.
    Leider funktioniert das mit der Schleife nicht wirklich.

    Gruß
    Blitzbirne


  • Mod

    Diese Schleife nützt Dir nichts! Denn die Statusabfrage funktioniert auch dann wen andere User diese Dateien benutzen.

    Wollen beide nur lesend zugreifen oder auch schreibend?
    Schau Dir mal die CFile::share... Flags an.

    Man kan das sperren der Datei auch intern durch CFile::LockRange handeln und den Zugriff darüber regeln.

    Aber grundsätzlich musst Du mit einem Fehler rechnen, warten und es erneut probieren.



  • Danke Martin,

    es wird lesend und schreibend darauf zugegriffen.
    Könntest du mir vielleicht ein kleines Beispiel posten?
    Schaue derweil mal nach den Flags (nehme an: MSDN)

    Danke


  • Mod

    Ich habe dafür kein Beispiel, das ich posten dürfte.

    Mit den share Flags kannst Du erlauben, dass mehrere Prozesse gleichzeitig eine Datei öffnen dürfen, jeweils ob lesend oder schreibender Zugriff verlangt wird.

    Ich regele das meistens mit LockRange und sperre nur den Bereich den ich benötige. Wenn die Datei wächst musst Du Dir einen entsprechenden multiuserfähigen Mechanismus ausdenken. Also DB-Like.

    Wenn es nur wenige daten zum lesen und schreiben betrifft und keine DB-like Applikation werden soll dann würde ich die Datei immer als ganzes Sperren (was jetzt auch passiert) im Konfliktfall die Sache einfach wiederholen. Das eignet sich natürlich nur dann, wenn die Updates nicht so oft kommen.

    Man kann auch das I/O über einen Service zentralisieren oder eine Datenbank verwenden. Ich kenne Dein Problem eben nicht genau genug.



  • Ich bin leider halt noch nicht so versiert, dass ich jetzt wirklich einen Ahaeffekt hätte 🙂

    Mein Problem stellt sich eigentlich ganz einfach dar:
    Ich öffne z.B. eine bestimmte Textdatei (C:\\test.txt) alle 5 Sekunden und Schreibe "test" hinein.
    Nun öffne ich genau diese Datei über einen anderen Thread.
    Dann darf natürlich nicht mein Testprogram in die Datei schreiben, da diese bereits anderweitig geöffnet ist.

    Wie verhindere ich das?

    Gibt's da keinen passenden 3Zeiler? :-)) Filehandling sollte doch eigentlich ziemlich easy sein. passiert ja eigentlich ständig auf diesem Planeten ...


  • Mod

    Du verhinderst das gar nicht. Du versuchst einfach auf beiden Seiten zu öfnen. Bei einem Fehler mit entsprechendem error code machst Du einfach einen retry!



  • Da habe ich mal so einen Try&Catch probiert.
    Hat leider nicht funktioniert.

    Bin ich da echt zu dämlich zu!? Probiere es bereits seit Tagen und krieg es nicht hin.

    Schade auch ...



  • Nun habe ich doch noch eine Möglichkeit für'n exklusiven Dateizugriff gefunden.
    Nur leider kann ich die Exception dazu nicht handeln.

    Timer der alle 20ms die Datei schreibt

    void CZugriffTestDlg::OnTimer(UINT nIDEvent) 
    {
    	if (nIDEvent == 1)
    	{
    		KillTimer(1);
    
    		SchreibeDatei();
    
    		SetTimer(1, 20, NULL);
    	}
    
    	CDialog::OnTimer(nIDEvent);
    }
    

    Die eigentliche Schreibroutine

    bool CZugriffTestDlg::SchreibeDatei()
    {
    	CFile SchreibDatei(Pfad, CFile::modeReadWrite | CFile::shareExclusive);
    	Inhalt = Inhalt + "Hallo\r\n";
    
    	try
        {
    		SchreibDatei.WriteHuge(Inhalt, Inhalt.GetLength());	
        }
        catch (CFileException &exception)
        {
    		AfxMessageBox("CATCH!!");
    		int *test  = &exception.m_cause;
    
    		//AfxMessageBox(e.m_cause);
            //if( e->m_cause == CFileException::fileNotFound )
        }
    
    	SchreibDatei.Close();
    
    	return true;
    }
    

    Mit der Schleife wird durch das ständige Aufrufen der Crash beider Schreibversuche provoziert ...

    void CZugriffTestDlg::OnOK() 
    {
    	SetTimer(1, 20, NULL);
    
    	while(SchreibeDatei())
    	{
    		Sleep(2);
    	}
    
    	//CDialog::OnOK();
    }
    

    eigentlich sollte es doch durch die Endlosschleife sowie das ständige 20ms Dateischreiben zur Zugriffsverletzung kommen.
    Warum komme ich nicht in die Catch-Routine rein????

    Gruß
    Blitzbirne


  • Mod

    Dein Fehler ist das Du eine Referenz fängst. Exceptions in der MFC werden als pointer geworfen.

    catch (CFileException *e) 
    {
    ...
    // Aufräumen nicht vergessen.
    e->Delete();
    


  • Super, vielen Dank!!
    Nun muß ich nur noch die Fehlermeldung des Systems unterdrücken.
    Wenn du da noch eine Idee hättest wäre ich dir sehr dankbar.
    Versuche es derweil schonmal selbst...


  • Mod

    Blitzbirne-C++ schrieb:

    Nun muß ich nur noch die Fehlermeldung des Systems unterdrücken.

    Was für eine Fehlermeldung?



  • Die Fehlermeldung welche das System in einer Messagebox ausgiebt:

    "Eine Zugriffsverletzung ist während des Zufriffs auf C:\zugriff.txt aufgetreten."

    Mein eigentliches Ziel ist es ja den Prozess, der momentan nicht zugreifen kann, in eine kleine Schleife zu schicken um direkt im Anschluß dann auf den File zuzugreifen.


  • Mod

    Wenn Du den ctach richtig setzt, gibt es keine MsgBox.
    Bitte poste noch mal Deinen Code.



  • Was ist denn ne CTACH? 😕

    void CZugriffTestDlg::OnTimer(UINT nIDEvent) 
    {
    	if (nIDEvent == 1)
    	{
    		KillTimer(1);
    
    		SchreibeDatei();
    
    		SetTimer(1, 20, NULL);
    	}
    
    	CDialog::OnTimer(nIDEvent);
    }
    
    bool CZugriffTestDlg::SchreibeDatei2()
    {
    	try
        {
    		CFile SchreibDatei(Pfad, CFile::modeReadWrite | CFile::shareExclusive);
    		Inhalt = Inhalt + "Hallo\r\n";
    		SchreibDatei.WriteHuge(Inhalt, Inhalt.GetLength());
    		SchreibDatei.Close();
        }
        catch (CFileException *exception)
        {
    		if(exception->m_cause == CFileException::sharingViolation)
    
    		exception->Delete();
        }
    
    	return true;
    }
    
    void CZugriffTestDlg::OnOK() 
    {
    	SetTimer(1, 20, NULL);
    
    	while(SchreibeDatei2())
    	{
    		Sleep(2);
    	}
    
    	//CDialog::OnOK();
    }
    
    bool CZugriffTestDlg::SchreibeDatei()
    {    
    	try
        {
    		CFile SchreibDatei(Pfad, CFile::modeReadWrite | CFile::shareExclusive);
    		Inhalt = Inhalt + "Hallo2\r\n";
    		SchreibDatei.WriteHuge(Inhalt, Inhalt.GetLength());
    		SchreibDatei.Close();
        }
        catch (CFileException *exception)
        {
    		if(exception->m_cause == CFileException::sharingViolation)
    
    		exception->Delete();
        }
    	return true;
    }
    

  • Mod

    Den

    if(exception->m_cause == CFileException::sharingViolation)
    

    vergiss!

    Was bezweckst Du damit?

    Ich sehe keinen Code, der hier eine MessageBox auslöst!



  • Das System, sprich Windows, schmeisst die MessageBox.
    Mit meinem Code provoziere ich ja lediglich diese Zugriffsverletzung und teste mal dieses Try/Catch.
    Was halt jetzt stört ist der Umstand, dass mein Prozess sich komplett aufhängt sobald Windows diese Zugriffsverletzung registriert.
    Daher möchte ich dieses gar nicht erst entstehen oder zulassen.

    Meine richtige Applikation besteht aus ziemlich vielen Klassen, welche zu unterschiedlichen Zeiten auf Dateien zugreifen. In diesen Klassen läuft auch hin und wieder mal ein Timer mit, aus dehnen heraus auch auf Dateien zugegriffen wird.

    Leider muß ich noch eine besondere DLL einbinden (Fremdanbieter) auf die ich definitiv nicht verzichten kann. Vorab: diese DLL findet man nicht im Netz.
    Der Thread der durch den Fremdanteil gestartet wird verkraftet diese Fehlermeldungen nicht und reisst die ganze Applikation ins Jenseits.

    Da ich über die selbst entwickelten Teile ja vollste Kontrolle habe würde ich gerne die Zugriffe daraus gerne solange ausbremsen bis der FremdThread fertig ist.

    Hoffe es ist verständlich 🙂


  • Mod

    Du faängst aber mit diesem Code die Exception ab.
    Dieser Code den Du zeigst kann keine Fehlermeldung anzeigen!

    Undich kan mich nur wiederholen. Um zu wissen ob eine datei offen ist musst Du sie öffnen und auf den fehler reagieren.

    Dein Code den Du hier gezeigt hast, fängt die Exception ab und kann keinen Messagebox anzeigen!



  • Ich glaube wir reden da ein wenig aneinander vorbei ...

    Windows hat scheinbar ein Handle auf den Prozess über das Windows weiss welcher Fehler aufgetreten ist.
    Sobald nun eine Zugriffsverletzung auftritt, da mehrere Prozesse gleichzeitig auf eine Datei zugreifen, gibt mir Windows dies per MessageBox bekannt.
    Ich brauche also nicht expliziet eine MessageBox zu generieren.
    Nur genau in dem Moment reisst es meine Applikation ins Nirvana.

    Kann man die Message unterdrücken?
    WIe kann ich es hinkriegen, dass es nicht zu diesen Kollisionen kommen kann?

    Gruß
    Günni



  • Ich bevorzuge diese Variante:

    CFile file;
    CFileException exept;
    if(file.Open(...,&exept))
    {
    ...
    }
    else
    {
       char expbuf[256];
       exceptp.GetErrorMessage((LPTSTR)expbuf, 255);
    ...
    }
    

    Hast Du es damit schon probiert?



  • Noch nicht.
    Probiere es eben mal aus ...


Anmelden zum Antworten