WNetAddConnection2() / WNetCancelConnectim Timing Problem
-
Hallo zusammen,
ich habe ein Programm, das Daten von verschiedenen Netzwerkfreigaben einsammeln soll. Da diese Freigaben kennwortgeschützt sind muss ich die entsprechenen Informationen per
WNetAddConnection2()
bereitstellen und nach erfolgreichem kopieren mitWNetCancelConnection()
wieder abräumen. Da nicht ausgeschlossen werden kann, dass Freigaben auf einem Server mit unterschiedlichen Benutzernamen freigeben werden habe ich folgenden Weg gewählt:void do_something() { for( int i = 0; i < Anzahl; ++i ) { ::WNetAddConnection2( ... ); ProcessFiles( ... ); ::WNetCancelConnection( ... ); } }
Dabei treten jetzt regelmässig zwei Probleme auf:
-
ProcessFiles
kehrt zurück, obwohl noch nicht alle Dateioperationen abgeschlossen sind, vermutlich weil das OS Datezugriffe cached und noch nicht alle Daten geschrieben/gelesen sind.WNetCancelConnection()
reißt dem OS die Freigabe unter´m Hintern weg, sodass es sich mit "Zugriff verweigert" wehrt. -
WNetCancelConnection()
trennt die Verbindung nicht sofort, sondern etwas verzögert. Der nächste Aufruf vonWNetAddConnection2
schlägt fehl, weil die alte Verbindung noch besteht und Windows für jeden Benutzer nur einen Zugriff auf Netzwerkfreigaben zulässt:
`
Mehrfache Verbindungen zu einem Server oder einer freigegebenen Ressource von demselben Benutzer unter Verwendung mehrerer Benutzernamen sind nicht zulässig. Trennen Sie alle früheren Verbindungen zu dem Server bzw. der freigegebenen Ressource, und versuchen Sie es erneut. [Error Code: 1219]
`
Hat jemand ´ne Idee, wie man das in den Griff kriegt? Eine Möglichkeit scheinen wohl die
AddConnectNotify/CancelConnectNotify
Funktionen zu sein, aber die muss man in einer DLL implementieren. Das und die zusätzliche Kommunikation mit der DLL sind mir erst ein Mal zu kompliziert, geht da nicht was Einfacheres?
-
-
Und warum trennst Du überhaupt die Verbindung, wenn Du im nächsten Moment die Verbindung wieder aufbaust?
Was ist mit einer Schleife um WNetCancelConnection, bis das eben klappt?
-
Hallo Martin,
danke für deine Antwort.
Martin Richter schrieb:
Und warum trennst Du überhaupt die Verbindung, wenn Du im nächsten Moment die Verbindung wieder aufbaust?
Weil sich die Zugangsdaten der Verbindung ändern können, d.h. ich kann mir nicht sicher sein, dass die Parameter für
WNetAddConnection()
gleich bleiben. Und da sich ein Benutzer nicht mehrmals mit unterschiedlichen Zugangsdaten an einem fremden Rechner anmelden darf (siehe Fehlermeldung #2) muss ich die Verbindung wieder trennen.Was ist mit einer Schleife um WNetCancelConnection, bis das eben klappt?
Hab´ ich mir auch schon überlegt, muss mal gucken, wie lange das dauern kann. Zur Not muss ich das sogar in einen eigenen Thread auslagern, um die Anwendung nicht zu blockieren. Aber ich wollte erst ein Mal warten, ob´s dazu nicht eine einfache Lösung gibt.
-
Interessant wäre zu sehen was du genau in ProcessFiles machst, dann könnte man evtl herausfinden wo das Problem liegt und ob man dein Problem abwenden kann.
Damit du nur wirklich dann die connection beendest wenn es nötig ist:
ConnectionDetails * details = 0; for( int i = 0; i < Anzahl; ++i ) { if( details != nextDetails ) { if( details ) { ::WNetCancelConnection( ... ); } ::WNetAddConnection2( ... ); details = nextDetails; } ProcessFiles( ... ); } ::WNetCancelConnection( ... );
Edit: Achja und natuerlich solltest du die connections nach servern und connection details sortieren
-
@evilissimo: +1
@DocShoe: Das Problem ist Dir aber klar, dass solch eine Connection Systemweit gilt... D.h. solange diese Connection besteht, können auch andeere Prozesse diese benutzen.
Und weiter: Hast Du es mit unterschiedlichen merhfachen Verbindungen zu einem anderen nicht Server (z.B. XP, Windows 7 etc) Rechner zu tun, dann macht der bei 10 Verbindungen dicht!
-
@evilissimo
Im Prinzip ist es eine Art Backup Tool, das Dateien von einer Quelle in einem Ziel sichert. Wenn es nur billiges Kopieren wäre könnte manCopyFile
mitFILE_FLAG_WRITE_THROUGH
benutzen, um das OS am Cachen zu hindern, aber eine mögliche Operation ist auch das Einpacken in ein Archiv. Dafür lässt sich das Cachen aber leider nicht deaktivieren, sodass ein Aufheben der Connection durchWNetCancelConnection
zu unerfreulichen Effekten führen kann (vom simplen "Zugriff verweigert" bis zum bösen "Datenverlust beim Schreiben").
Ich könnte natürlich vor der Ausführung jedes Jobs prüfen, ob er auf die Quelle zugreifen kann (weil der vorherige Job die Verbindung nicht gekappt hat und sie für den aktuellen Job noch besteht). Aber genau das tritt ja nur dann ein, wenn der vorherige Job nicht fehlerfrei abgearbeitet wurde, und davon möchte ich nicht ausgehen, weil es die Ausnahme und nicht die Regel sein soll.
In deinem Beispiel kappst du die Verbindung nur dann, wenn es nötig ist. Ich möchte aber das Gegenteil, nämlich dass die Verbindung nur dann existiert, wenn sie benötigt wird.Für die ProcessFiles() Details muss ich etwas weiter ausholen:
In einer Queue sind Jobs abgelegt, die bestimmte Operationen ausführen. Jeder Job hat eine Quelle, ein Ziel und optional Zugangsdaten für Quelle und Ziel. Jeder Job hat wiederum eine Queue von Commands, die bestimmte Operationen durchführen, z.B. das Kopieren oder Einpacken von Dateien. Dieexecute
Methode eines Jobs sieht etwa so aus:void Job::execute() { WNetAddConnection2( ... ); try { foreach( Command in Commands_ ) { Command->execute(); } } catch( exception& expc ) { rollback(); } ::WNetCancelConnection2( ... ); }
Die einzelnen Jobs und Commands kennen sich untereinander nicht und können daher auch nicht wissen, ob der Vorgänger die gleichen Zugangsdaten benutzt. Das sollte eigentlich aus so bleiben...
@Martin
Mir ist schon klar, dass die Verbindungen systemweit gelten, deshalb sollten sie auch so kurzlebig wie möglich sein, um Missbrauch zu vermeiden. Im Idealfall sollte die Verbindung nur für die Dauer der Abarbeitung des Jobs bestehen (was dann auch das Problem der maximalen Anzahl der gleichzeitigen Verbindungen löst). Irgendwie verstehe ich deinen Einwand nicht, mir geht´s ja nicht darum, möglichst viele Verbindungen aufzumachen, sondern die Anzahl möglichst klein und kurzlebig zu halten.Inzwischen habe ich das Problem darauf reduzieren können, dass es tatsächlich am Cachen von Dateioperationen liegt. Man kann
WNetCancelConnection2
durch den dritten Parameter erzwingen, und nur in diesem Fall bekomme ich schwere Fehler wie z.B. "Datenverlust beim Schreiben". Also lasse ich zu, dassWNetCancelConnection2
die Verbindung nicht trennt, wenn sie noch benutzt wird. Das verhindert kritische Fehler, lässt eine Verbindung manchmal aber länger leben, als eigentlich notwendig ist. Das führt dann auch manchmal zu Fehler #2 aus meinem Eingangsposting, womit ich aber ganz gut leben kann. Wenn dieser Fehler auftritt wird der Job zurückgesetzt und später ein zweites oder drittes Mal ausgeführt, bis es irgendwann klappt. Ist zwar nicht schön, weil in den Logfiles dann Fehlermeldungen auftauchen, aber das muss ich wohl akzeptieren.