Entwickler-Ecke
Windows API - CopyFile / ForceDirectories - Pfadlänge
s!lenCe - Di 03.11.09 10:01
Titel: CopyFile / ForceDirectories - Pfadlänge
Hallo allerseits,
folgendes Problem:
ich will eine Datei (bzw. mehrere) kopieren, dessen Zielpfad (mit Dateinamen) die Begrenzung von 255 Zeichen
überschreitet. Ich habe CopyFile (CopyFileW, CopyFileA) versucht, jedoch kopiert er mir die Datei damit nicht.
Gibt es eine Alternative zu CopyFile um soetwas realisieren zu können? Oder ist CopyFile mit irgendwelchen Tricks
vielleicht doch dazu in der Lage? Mal jetzt davon abgesehen, dass der Explorer selbst nicht mit einem Pfad klar kommt,
der über 255 Zeichen lang ist.
MfG silenCe
Delete - Di 03.11.09 10:38
Versuch es mal mit UNC Pfadangaben. Ich glaube mal gehört zu haben, dass man damit die Längenbegrenzung der Pfadangabe (260 Zeichen) umgehen kann. Wahrscheinlich musst du dann aber auch CopyFileW benutzen.
s!lenCe - Di 03.11.09 11:14
Luckie hat folgendes geschrieben : |
Versuch es mal mit UNC Pfadangaben. Ich glaube mal gehört zu haben, dass man damit die Längenbegrenzung der Pfadangabe (260 Zeichen) umgehen kann. Wahrscheinlich musst du dann aber auch CopyFileW benutzen. |
Das habe ich auch schon irgendwo gelesen und habe es auch schon ausprobiert, bin aber nicht ganz sicher, ob das so richtig war.
Delphi-Quelltext
1:
| CopyFileW(PWideChar(Quelle), PWideChar('\\?\' + Ziel),false) |
Wie gesagt das mit dem '\\?\' habe ich irgendwo gelesen, da hat es sich aber auf CreateFile bezogen. Ich habe auch versucht das beiden voranzustellen,
also Quelle und Ziel, obwohl nur das Ziel die Länge überschreitet. Hat aber alles nicht funktioniert.
Mit fällt gerade auf ich brauch das selbe auch für ForceDirectories, da ich mit dieser Funktion erst die Verzeichnisse anlege und dann die Datei kopiere.
Und ForceDirectories legt mir auch nicht alle Verzeichnisse an nur bis zu dem Punkt an dem die 260 Zeichen voll sind.
MfG silenCe
Gausi - Di 03.11.09 11:24
wikipedia hat folgendes geschrieben: |
Das Format für lokale Pfade unter Windows, also Daten, die auf dem verwendeten Rechner selbst vorhanden sind, ist:
|
Hast du das mal so ausprobiert?
Edit: Bei deinem Konstrukt ist auch wichtig: Ziel und Quelle müssen WideStrings sein. Der Typecast von Delphi-2007-String nach PWideChar ergibt Unsinn.
Unter D2009 kannst du mit der CopyFile und PChar-Version arbeiten. Da sind das ja die Unicode-Varianten.
s!lenCe - Di 03.11.09 11:37
Gausi hat folgendes geschrieben : |
wikipedia hat folgendes geschrieben: | Das Format für lokale Pfade unter Windows, also Daten, die auf dem verwendeten Rechner selbst vorhanden sind, ist:
| Hast du das mal so ausprobiert?
Edit: Bei deinem Konstrukt ist auch wichtig: Ziel und Quelle müssen WideStrings sein. Der Typecast von Delphi-2007-String nach PWideChar ergibt Unsinn.
Unter D2009 kannst du mit der CopyFile und PChar-Version arbeiten. Da sind das ja die Unicode-Varianten. |
Nutze Delphi7 und wenn ich keinen Typecase mache bzw. PChar nehme meckert der Compiler. Also ich habe jetzt auf jeden Fall rausgefunden, dass er die Datei
sowieso gar nicht kopieren kann, da das ForceDirectories, das vor dem Kopieren ausgeführt wird, um die Verzeichnisstruktur für die Datei anzulegen, auch fehlschlägt
mit der Fehlermeldung der angegebene Pfad wäre zu lang - ist er ja auch eigentlich :).
So habe während ich den Beitrag geschrieben habe nun das mit dem ForceDirectories hinbekommen.
Delphi-Quelltext
1:
| ForceDirectories('\\?\' + Pfad) |
So legt er mir einen Pfad an, der auch länger als 260 Zeichen ist. Jetzt muss ich nur noch das CopyFile ans Laufen kriegen.
MfG silenCe
Gausi - Di 03.11.09 11:39
Wie gesagt, der direkte Typecast von String nach PWideChar ergibt Unsinn. Wenn du normale Strings unter D7 benutzt, dann muss das so aussehen:
Delphi-Quelltext
1:
| PWideChar(WideString(myString)) |
s!lenCe - Di 03.11.09 11:55
Gausi hat folgendes geschrieben : |
Wie gesagt, der direkte Typecast von String nach PWideChar ergibt Unsinn. Wenn du normale Strings unter D7 benutzt, dann muss das so aussehen:
Delphi-Quelltext 1:
| PWideChar(WideString(myString)) | |
Es funktioniert! Endlich ~.~
Puhh ich danke dir ^^
Also der Code sieht nun wie folgt aus.
Einmal die Verzeichnisstruktur mit ForceDirectories anlegen und dann die Datei kopieren. Beide kommen nun mit Pfadangaben, die länger als 260 Zeichen sind, klar.
Delphi-Quelltext
1: 2: 3:
| ForceDirectories('\\?\' + ExtractFilePath(Pfad))
CopyFileW(PWideChar(WideString(Quelle)),PWideChar(WideString('\\?\' + Ziel)),false) |
EDIT:
Verdammt habe mich wohl zu früh gefreut. Habe nochmal einen längeren Pfad angelegt (genau 265 Zeichen) und daran scheitert ForceDirectories doch mit der Fehlermeldung
der Pfad sei zu lang. -.-
MfG silenCe
Delete - Di 03.11.09 13:23
Wahrscheinlich weil ForceDirectories keine Widestrings benutzt. Da wirst du dir wohl deine eigene Unicode Variante implementieren müssen.
Delete - Di 03.11.09 14:22
Musst du wahrscheinlich selbst deklarieren oder probier mal Shlobj.
s!lenCe - Di 03.11.09 16:46
So habe mir jetzt einfach meine eigene Variante geschrieben.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
| FUNCTION ForceDirectoriesW(Dir : WideString) : Boolean; VAR tmp : WideString; Position : Integer; BEGIN Result := True; Dir := ExtractFilePath(Dir); Dir := IncludeTrailingPathDelimiter(Dir); tmp := Copy(Dir, 1, 3); Delete(Dir, 1, 3);
WHILE Pos('\', Dir) <> 0 DO BEGIN Position := Pos('\', Dir); tmp := tmp + Copy(Dir, 1, Position); Delete(Dir, 1, Position); IF NOT DirectoryExists(tmp) THEN IF NOT CreateDirectoryW(PWideChar(WideString('\\?\' + tmp)), nil) THEN BEGIN Result := False; Exit; END; END; END; |
MfG silenCe
beastofchaos - So 22.05.11 11:25
Ich hab leider auch ein kleines Problem mit CopyFile() - Und zwar soll man in meinem Programm ein Lied (mp3 oder wav) kopieren können. Habe einmal einen Button "Browse", einen anderen "Add".
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53:
| path := ExtractFilePath(Application.ExeName); . . .
procedure TSoundForm.BrowseClick(Sender: TObject); var Endung: String; begin if dlgOpen.Execute then begin NewSong := dlgOpen.FileName; Endung := Copy(NewSong, length(NewSong) - 2, 3); if (Endung <> 'mp3') and (Endung <> 'wav') then begin ShowMessage('Das Lied muss eine mp3- oder wav-Datei sein!'); BrowseClick(Button4); Exit; end else begin Edit1.Text := NewSong; Selected := True; end; end; end;
procedure TSoundForm.AddClick(Sender: TObject); var NewSongName: String; i, i2: Integer; Finished: Boolean; begin Finished := False; if Selected then begin Dec(i, i); repeat begin NewSongName := Copy(NewSong, length(NewSong) - i, 1 + i); if NewSongName[1] = '\' then begin NewSongName := copy(NewSongName, 2, length(NewSongName) - 1); Finished := True; end; Inc(i) end; until Finished; CopyFile(PChar(NewSong),PChar(path + 'Sounds\Background\' + NewSong), False) end else ShowMessage('Bitte wähle erst eine Datei aus!'); end; |
Es kommt KEINE Fehlermeldung, aber andererseits lässt sich unter Sound\Background kein neues lied finden... Geht das möglicherweise nicht mit mp3, etc. - ich hab das bisher nur mit *txt gesehen.
Gruß, Thomas
jaenicke - So 22.05.11 12:16
beastofchaos hat folgendes geschrieben : |
Es kommt KEINE Fehlermeldung |
Woher auch, die hast die Fehlerbehandlung ja auch komplett ignoriert... :roll: :autsch:
Den Rückgabewert wirfst du genauso weg wie du danach bei einem Fehlschlag nicht mit SysErrorMessage + GetLastError schaust warum es nicht ging... :roll:
Siehe Doku...
http://msdn.microsoft.com/en-us/library/aa363851(v=vs.85).aspx
beastofchaos - So 22.05.11 12:51
jaenicke hat folgendes geschrieben : |
beastofchaos hat folgendes geschrieben : | Es kommt KEINE Fehlermeldung | Woher auch, die hast die Fehlerbehandlung ja auch komplett ignoriert... :roll: :autsch:
Den Rückgabewert wirfst du genauso weg wie du danach bei einem Fehlschlag nicht mit SysErrorMessage + GetLastError schaust warum es nicht ging... :roll:
Siehe Doku...
http://msdn.microsoft.com/en-us/library/aa363851(v=vs.85).aspx |
Mit Fehlermeldung mein ich cnith die von Delphi, sondern meine Eigene (siehe Quelltext Zeile 16 :P)
Nachdem ich mir die Namen der SoundNames ausgegeben hab lassen, ist mir aufgefallen, dass ich am Ende die Datei abspeicher mit dem falschen Namen (In Zeile 49 statt "+ NewSong" -> "+ NewSongName").
Das Problem kontne ich also lösen. Ich hab jetzt leider noch ein Problem ,dass nichts mit der CopyFile()-Prozedur zu tun hat, aber in diesem Programmteil vorkommt. Und zwar speicher ich mit der Prozedur "FindFiles()" aus unser Liberary in der TStringList "SoundNames". Jedesmal, wenn ich nun ein Liad hinzufpge mit "Add" speicher ich alles in einer temporören TStringList namens SoundNamesBefore, um abzugleichen, ob ich das Lied shcon einmal hochgeladen habe - Damit will ich nur die neuen Lieder in meine TStringList laden. Quelltext sieht so aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
| . . . else begin SoundNamesBefore := SoundNames; SoundNames.Clear; There := False; FindFiles(path + 'Sounds\Background', '*.mp3', False, SoundNames); for i := 0 to SoundNames.Count - 1 do begin for i2 := 0 to SoundNamesBefore.Count - 1 do if (SoundNames[i] = SoundNamesBefore[i2]) then There := True; if not There then begin Memo1.Lines.Add('1'); SetLength(FBackgroundStream, length(FBackgroundStream) + 1); FBackgroundStream[i] := BASS_StreamCreateFile(false, PAnsiChar(AnsiString(SoundNames[i])), 0, 0, 0); end else Memo1.Lines.Add('0'); There := False; end;
Memo1.Lines := SoundNamesBefore; Memo1.Lines.Add('------------'); Memo1.Lines.AddStrings(SoundNames); end; |
Für wen das jetzt zu unverständliches Gelafer war: Ich hab im Moment 4 Pfade(Strings) in SoundNames - die speicher ich dann in SoundNamesBefore und clear SoundNames. Dann ermittel ich mit "FindFiles" alle Namen in dem gesuchten Ordner. Dadurch dass ich eins hinzugefügt habe, sind nun 5 Pfade in SoundNames.
Wenn ich jetzt am Ende SoundNamesBefore mal ausgebe, hat es FÜNF Einträge.
Ist die StringList vll. durch Zeile 6 in meinem Quelltext eine "Referenzvariable geworden???". Und wie löse ich das Problem
Gruß, Thomas
jaenicke - So 22.05.11 13:06
Eine TStringList ist ein Objekt und damit immer nur eine Referenz auf das Objekt.
Da musst du eine neue Instanz erstellen und den Inhalt kopieren, wenn du eine Kopie möchtest.
beastofchaos - So 22.05.11 13:10
Was meisnt du mit Instanz?
Auch wenn das in meinen Augen unsauber ist, werd ich einfach die Memo unsichtbar machen lassen, "SoundNamesBefore" da rein schreiben und am Ende das wieder in SoundNamesBefore speichern :P Da könnte ich eigentlich sogar SoundNamesBefore ganz weglassen :)
Dude566 - So 22.05.11 13:19
Eine Instanz ist ein erzeugtes Exemplar einer Klasse(Objekt).
beastofchaos - So 22.05.11 13:51
Dude566 hat folgendes geschrieben : |
Eine Instanz ist ein erzeugtes Exemplar einer Klasse(Objekt). |
Wie sieht das dann im Quelltext aus? xD
Ich verstehe da jetzt nicht den Unterschied zu meinem erzeugten Objekt(TStringList)
jaenicke - So 22.05.11 14:01
Dein Objekt
ist eine Instanz der Klasse. ;-)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| var List1, list2: TStringList; begin List1 := TStringList.Create; try List1.Add('a'); List2.Add('b'); List2 := TStringList.Create; try List2.Assign(List1); ShowMessage(List2[0]); finally List2.Free; end; finally List.Free; end; |
Du hast eine Instanz von TStringList in List1 und eine in List2.
beastofchaos - So 22.05.11 14:23
Deine Prozedur da gibt mir och "a" aus, oder? Weil du es mit Assign() zuweist - ich vermute mal, dass Assign auch etwas zu einer Referenzvariable macht, obwohl ich das bisher nur vom Textdateien schreiben und auslesen kenne.
Wie mache ich es denn dann richtig? Weil ich iwie nicht den Sinn in deiner Prozedur sehe. Ich würds so machen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| List1 := TStringList.Create; List2 := TStringList.Create;
List2 := List1 List1.Add('a');
ShowMessage(List2[0]); List1.Free; List2.Free; |
jaenicke - So 22.05.11 14:31
Nein, das macht keine Referenz, es kopiert nur den bisherigen Inhalt.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| var List1, list2: TStringList; begin List1 := TStringList.Create; try List1.Add('a'); List2.Add('b'); List2 := TStringList.Create; try List2.Assign(List1); List1[0] := 'weder a noch b'; ShowMessage(List2[0]); finally List2.Free; end; finally List.Free; end; |
Es kommt immer noch a heraus, da die beiden Instanzen nicht voneinander abhängen.
beastofchaos - So 22.05.11 14:59
jaenicke hat folgendes geschrieben : |
Nein, das macht keine Referenz, es kopiert nur den bisherigen Inhalt. Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| var List1, list2: TStringList; begin List1 := TStringList.Create; try List1.Add('a'); List2.Add('b'); List2 := TStringList.Create; try List2.Assign(List1); List1[0] := 'weder a noch b'; ShowMessage(List2[0]); finally List2.Free; end; finally List.Free; end; | Es kommt immer noch a heraus, da die beiden Instanzen nicht voneinander abhängen. |
GENIAL!!
Danke :) Also statt " := " die Prozedur Assign() nehmen - dankeschöööön
Gruß, Thomas
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!