Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch? (https://www.delphipraxis.net/202995-tshfileopstruct-wieviele-null-terminatoren-braucht-der-mensch.html)

Guido Eisenbeis 1. Jan 2020 20:58

TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Nachdem ich ewig lange recherchiert habe, wie man komplette Ordnerinhalte kopiert (alle Dateien und Unterordner), habe ich nicht wirklich herausfinden können, wieviele Null-Terminatoren bei den Pfaden im TSHFileOpStruct benötigt werden.

Microsoft sagt unter Remarks, Important: "You must ensure that the source and destination paths are double-null terminated. ..."

Manche Codes im INet benutzen TStrings oder AnsiString, andere benutzen TFileName. Die einen terminieren pFrom und pTo überhaupt nicht zusätzlich, andere mit 1 zusätzlichen #0, und wiederum andere mit 2 zusätzlichen #0. Erstaunlicherweise sind die meisten Beiträge, die ich dazu gefunden habe, aus den frühen 2000er Jahren. Da ist ja viel Zeit vergangen. Wie sieht das denn heute aus? Welcher Eingangs-Dateityp (für Source- und Destination-Path) muss mit wie vielen Terminatoren ergänzt werden, wenn er mit PChar() an pFrom und pTo übergeben wird?

Im Moment benutze ich den folgenden Code, der TFileName als Typ für Source und Destination verwendet. Dabei werden keine zusätzlichen Null-Terminatoren benutzt. Ist das überhaupt in Ordnung? Leider konnte ich diesbezüglich keine Informationen zu TFileName finden.

Delphi-Quellcode:
// Routine zum Kopieren von Dateien und Ordnern.
function SHCopyFile(Handle: THandle; szSource, szDestination: TFileName): Boolean;
var
  ShellFileOperation: TSHFileOpStruct;
begin
  Result := True;
  try
    with ShellFileOperation do
    begin
      Wnd := 0;
      wFunc := FO_COPY;
      pFrom := PChar(szSource);
      pTo := PChar(szDestination);
      fFlags := FOF_NOCONFIRMMKDIR or FOF_NOCONFIRMATION;
    end;
    SHFileOperation(ShellFileOperation);
  except
    Result := False;
  end;
end;
Guido.

Dalai 1. Jan 2020 21:36

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Eigentlich steht alles in dem verlinkten MS-Artikel drin:
Zitat:

pFrom

Type: PCZZTSTR

[...]
Although this member is declared as a single null-terminated string, it is actually a buffer that can hold multiple null-delimited file names. Each file name is terminated by a single NULL character. The last file name is terminated with a double NULL character ("\0\0") to indicate the end of the buffer.
Damit die API-Funktion weiß, wo das Ende des Buffers ist, sind zwei NULLs nötig.

Im Delphi würde ich das wohl so machen:
Delphi-Quellcode:
PChar(szSource + #0);
denn IIRC hängt der Cast zu PChar bereits ein NULL an.

Grüße
Dalai

himitsu 1. Jan 2020 21:52

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Delphi hängt standardmäßig immer zwei Nullen an seine Strings an. (als Kompatibilität auch zu solchen Listen)
Also im Prinzip könnte man es hier auch weglassen, aber zur Dokumentation hänge ich auch meistens die richtige Anzahl der Nullen nochmals an, also diese #0#0 für Listen. (nur bei "einfachen" PChars lasse ich diese #0 weg)

Und ja, hier sind es "eigentlich" zwei #0,
denn es ist eine Liste von Pfaden, wobei als Listenende ein Leerstring definiert ist.
Delphi-Quellcode:
'Datei1'#0 + 'Datei2'#0 + {ENDE} ''#0;
, also effektiv #0#0 am Ende.

PS: Die RTTI nutzt sowas ebenfalls, z.B. bei der Namensliste für ENUMs,
oder die StringListen in den Ressources.


PS: ein PChar-Cast, von einem String aus, gibt immer einen "gültigen" Zeiger zurück.
Ein leerer String ist zwar NIL, aber hier wird dann ein Zeiger auf eine Konstante mit #0#0 zurückgegeben.
Wer für eine Funktion ein "echtes" NIL benötigt, wenn der String leer ist, der sollte mit Pointer casten
Delphi-Quellcode:
Pointer(MyString)
statt
Delphi-Quellcode:
PChar(MyString)
.
(einziger Nachteil des Pointer, dabei geht die Typprüfung bezüglich ANSI und Unicode verloren)

Guido Eisenbeis 2. Jan 2020 00:53

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Zitat:

Zitat von himitsu (Beitrag 1454269)
Delphi hängt standardmäßig immer zwei Nullen an seine Strings an. (als Kompatibilität auch zu solchen Listen)
Also im Prinzip könnte man es hier auch weglassen, ...

Zitat:

Zitat von himitsu (Beitrag 1454269)
PS: ein PChar-Cast, von einem String aus, gibt immer einen "gültigen" Zeiger zurück.
Ein leerer String ist zwar NIL, aber hier wird dann ein Zeiger auf eine Konstante mit #0#0 zurückgegeben.

Zitat:

Zitat von Dalai (Beitrag 1454267)
Im Delphi würde ich das wohl so machen:
Delphi-Quellcode:
PChar(szSource + #0);
denn IIRC hängt der Cast zu PChar bereits ein NULL an.

Delphi-Quellcode:
PChar(szSource + #0);
Wie viele #0's ergibt das denn?


Gibt es denn auch "zuviele" #0's? Wenn ein string in Delphi schon standardmäßig 2 #0 hat und ich z. B. eingebe:
Delphi-Quellcode:
  pFrom := PChar(szSource + #0#0);
  pTo := PChar(szDestination + #0#0);
Kann es dann passieren, dass pFrom nun 4 #0 am Ende hat, und die Funktion das als Leerstring für pTo auswertet?

Wie sieht das denn mit TFileName aus, also mit wie vielen #0's ist TFileName abgeschlossen?

Dalai 2. Jan 2020 01:16

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Zitat:

Zitat von Guido Eisenbeis (Beitrag 1454272)
Wie viele #0's ergibt das denn?

Mindestens zwei.

Zitat:

Gibt es denn auch "zuviele" #0's?
Kommt auf den Anwendungszweck an. Meistens nein, und in diesem Fall definitiv nicht.

Zitat:

Wenn ein string in Delphi schon standardmäßig 2 #0 hat und ich z. B. eingebe:
Delphi-Quellcode:
  pFrom := PChar(szSource + #0#0);
  pTo := PChar(szDestination + #0#0);
Kann es dann passieren, dass pFrom nun 4 #0 am Ende hat, und die Funktion das als Leerstring für pTo auswertet?
Nein. Wie bei MS beschrieben, liest die API-Funktion bis zum ersten doppelten NULL und stoppt dort. Die Einzelstrings sind mit einfachen NULLs voneinander getrennt.

Zitat:

Wie sieht das denn mit TFileName aus, also mit wie vielen #0's ist TFileName abgeschlossen?
TFileName ist nur ein Alias für einen
Delphi-Quellcode:
string
. Strings sind in Delphi AFAIK nicht nullterminiert (sie haben aber im Gegensatz zu nullterminierten Strings eine Längenangabe).

Grüße
Dalai

Guido Eisenbeis 2. Jan 2020 01:47

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Zitat:

Zitat von himitsu (Beitrag 1454269)
Delphi hängt standardmäßig immer zwei Nullen an seine Strings an.

Zitat:

Zitat von Dalai (Beitrag 1454273)
Strings sind in Delphi AFAIK nicht nullterminiert ...

Ei-jei-jei, was denn nu'? :wiejetzt:

Guido Eisenbeis 2. Jan 2020 01:55

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
PChar(szSource + #0) ==> Wie viele #0's ergibt das denn? ==> Mindestens zwei.

Gibt es denn auch "zuviele" #0's? ==> Kommt auf den Anwendungszweck an. Meistens nein, und in diesem Fall definitiv nicht.

Kann es dann passieren, dass pFrom nun 4 #0 am Ende hat, und die Funktion das als Leerstring für pTo auswertet? ==> Nein. Wie bei MS beschrieben, liest die API-Funktion bis zum ersten doppelten NULL und stoppt dort. Die Einzelstrings sind mit einfachen NULLs voneinander getrennt.

... mit wie vielen #0's ist TFileName abgeschlossen? ==> TFileName ist nur ein Alias für einen
Delphi-Quellcode:
string
. Strings sind in Delphi AFAIK nicht nullterminiert (sie haben aber im Gegensatz zu nullterminierten Strings eine Längenangabe).

@Dalai Vielen Dank für die deutlichen Aussagen! :)

himitsu 2. Jan 2020 02:11

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Jain.
Delphi-Strings sind per-se nicht null-terminiert, da ihre Länge gespeichert und von Delphi verwendet wird.
Beim ShortString (dem "String" aus TurboPascal und dem ersten Delphi) gibt es nur das Längen-Byte und keine explizite #0 am Ende.

Bei den LongStrings (AnsiString/UTF8String/RawByteString/... und UnicodeString) gibt es aber als Optimierung auch die #0#0 am Ende, damit es ohne umkopieren direkt nach PChar gecastet werden kann.
Der WideString ... OLE32-String (MSDN-Library durchsuchenSysAllocStringLen) hat auch eine Längenangabe und die #0#0 (nur ihm fehlt die Referenzzählung, welche netter Weise die Delphi-Strings besitzen).

Also Delphi/Pascal verwendet bei String-Operationen nur die Längenangabe, aber bei der Verwaltung wird hinter dem letzten Zeichen noch ein Bereich mit den #0#0 bereitgehalten.

Beispiel: StrLen im C++ liest den String Zeichen für Zeichen und sucht die erste #0 (Ende-Anfang=Länge), während Length direkt den Längen-Integer ausliest.

Guido Eisenbeis 2. Jan 2020 02:32

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Zitat:

Zitat von Dalai (Beitrag 1454273)
TFileName ist nur ein Alias für einen
Delphi-Quellcode:
string
.

Zitat:

Zitat von Aus der Delphi Hilfe
Der Typ String ist ein Alias für UnicodeString.

Von diesen ganzen Aliasen (Aliases, Alianten, ... Aliens?) schwirrt mir der Kopf. Ich mache es jetzt einfach so:

function SHCopyFile(Handle: THandle; szSource, szDestination: TFileName): Boolean;
...
pFrom := PChar(szSource + #0);
pTo := PChar(szDestination + #0);

Vielen Dank für eure Infos! :)

himitsu 2. Jan 2020 02:41

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
TFileName ist kein Alias, es ist ein Nachfahre. :zwinker:

Delphi-Quellcode:
type
  TFileName = type string;
              ^^^^
Definiert einen neuen Typ mit den selben Eigenschaften, welcher auch zuweisungskompatibel ist.

String ist sowas, wie Integer früher und NativeInt jetzt ist ... er ist ein Alias auf den nativen Typen des Compilers.
* vor Delphi2009 war es der AnsiString
* und gaaaaaanz früher war es kein Alias, da war es der Typ String, welcher jetzt ShortString heißt (aber der Einfachheit halber könnte man es da als Alias für den ShortString sehen)

Luckie 2. Jan 2020 04:25

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Debug es doch und lass dir anzeigen, was in den Variablen drin steht.

Guido Eisenbeis 2. Jan 2020 07:39

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Luckie, du bist der Beste! :thumb: Die ganze Zeit habe ich überlegt, wie ich das damals anzeigen lassen habe und bin nicht aufs Debuggen gekommen. Kennst du das, wenn du weißt, da war doch was, aber du kommst nicht drauf was? :gruebel: Auch wenns leider nicht funktioniert, (siehe Screenshot), weiß ich jetzt wenigstens was mir nicht mehr einfallen wollte! Vielen Dank.

Ich weiß aber auch, dass das damals ging, da wurde im Tooltip über den Variablen auch die #0's angezeigt. Muss ich noch irgendwas einstellen, damit das angezeigt wird?

Luckie 2. Jan 2020 08:15

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Zitat:

Ich weiß aber auch, dass das damals ging, da wurde im Tooltip über den Variablen auch die #0's angezeigt.
Genau daran dachte ich. Ich habe allerdings nur das BDS2006. Da wird das, meine ich, so angezeigt.

Guido Eisenbeis 2. Jan 2020 08:20

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Genau! :-D Bei mir wars Turbo Delphi 2006! Hach ja, die alten Zeiten. ...

himitsu 2. Jan 2020 14:40

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Zitat:

Ich weiß aber auch, dass das damals ging, da wurde im Tooltip über den Variablen auch die #0's angezeigt.
Wenn die #0 im String enthalten ist (innerhalb des Length), dann gehört sie zum String und wird auch angezeigt.

Guido Eisenbeis 2. Jan 2020 20:28

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Hast du eine Idee, warum bei mir kein #0 angezeigt wird? (siehe Screenshot weiter oben)

hoika 2. Jan 2020 20:40

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Hallo,
Zitat:

wenn ich die Maus drüberhalte, wird nichts angezeigt.
Das liegt am with.

Guido Eisenbeis 2. Jan 2020 21:02

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von hoika (Beitrag 1454334)
Hallo,
Zitat:

wenn ich die Maus drüberhalte, wird nichts angezeigt.
Das liegt am with.

Hallo hoika, gute Idee! Ich dachte schon, jetzt funktionierts, aber leider kein Erfolg. :cry:

Nachtrag: Halt, Kommando zurück! Da gab es ein Missverständnis meinerseits. Ich hatte im Kopf, dass die #0's angezeigt werden, aber du hast ja nur geschrieben, dass Tooltips angezeigt werden, wenn mit der Maus drübergehalten wird. Und DAS funktioniert. Vielen Dank für den Tipp! :-D

Leider werden trotzdem keine #0's angezeigt. :cry:

Dalai 2. Jan 2020 21:38

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Ich denke, dass solcherlei Zeichen nur dann angezeigt werden, wenn sie mittendrin im String sind, nicht am Ende.

Grüße
Dalai

himitsu 6. Jan 2020 01:58

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Im "String" ist die #0 drin, aber deine Variable (bzw. der Teil des Records), welche du anzeigen lässt,
ist ein PChar und der ist natürlich 0-terminiert,
also endet hier die Auswertung somit bei/vor der ersten #0, unabhängig davon ob der Speicher, auf welchen dieser PChar zeigt, mehr Nullen enthält.

Das hat dann natürlich nichts damit zu tun, dass die verwendete API diesen PChar-Zeiger als Liste auswertet.
Es wäre natürlich praktisch, wenn es dafür einen eigenen Listen-PChar-Typen gäbe, wo dann auch der Debugger weiß, dass bei diesem Typen eine #0 enthalten sein kann und die Anzeige erst beim #0#0 endet.

Guido Eisenbeis 6. Jan 2020 02:10

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Danke für die Info! :)

Luckie 6. Jan 2020 07:31

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Zitat:

Zitat von himitsu (Beitrag 1454473)
Im "String" ist die #0 drin, aber deine Variable (bzw. der Teil des Records), welche du anzeigen lässt,
ist ein PChar und der ist natürlich 0-terminiert,
also endet hier die Auswertung somit bei/vor der ersten #0, unabhängig davon ob der Speicher, auf welchen dieser PChar zeigt, mehr Nullen enthält.

Das hat dann natürlich nichts damit zu tun, dass die verwendete API diesen PChar-Zeiger als Liste auswertet.
Es wäre natürlich praktisch, wenn es dafür einen eigenen Listen-PChar-Typen gäbe, wo dann auch der Debugger weiß, dass bei diesem Typen eine #0 enthalten sein kann und die Anzeige erst beim #0#0 endet.

Also einen Datentyp nur für den Debugger?

himitsu 7. Jan 2020 15:02

AW: TSHFileOpStruct - Wieviele Null-Terminatoren braucht der Mensch?
 
Auch als Dokumentation für den Entwickler, denn bei PChar bzw. LPCSTR usw. denkt doch jeder erstmal an "endet mit #0".

Gäbe es einen eigenen Typen, dann wüssten Entwickler und Debugger sofort, dass es hier bis #0#0 geht. (ohne die Dokumentation für diese spezielle Variable/Parameter zu kennen)
Wie z.B. beim TDateTime, der ja eigentlich auch nur ein Double ist, den der Debugger aber "verständlicher" Darstellen kann. :zwinker:


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:48 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz