Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Compiler ersetzt Variable durch Konstante - AV zur Laufzeit (https://www.delphipraxis.net/177536-compiler-ersetzt-variable-durch-konstante-av-zur-laufzeit.html)

Der schöne Günther 12. Nov 2013 12:52


Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Eine WinAPI-Methode erwartet einen PWideChar den sie eventuell auch verändern wird. Meine Methode hat dafür eine lokale Variable des Typs. Sieht so aus:

Delphi-Quellcode:
const
   defaultStrValue: String = 'Das ist ein String';
var
   myString: String;
   myWideChar: PWideChar;
begin

   myString := defaultStrValue;
   myWideChar := PWideChar(myString);
   
   WinApiAufruf(myWideChar); // AV
end;
Die Zugriffsverletzung führe ich eindeutig auf einen Schreibversuch auf die Konstante zurück. Der Compiler sieht wohl "He, die Variable ist ja genau das gleiche wie die Konstante" und wird nicht die Variable
Delphi-Quellcode:
myString
sondern die Konstante
Delphi-Quellcode:
defaultStrValue
casten.
Jetzt das kuriose: Ändere ich die myWideChar-Zuweisung mit diesen drei Zeichen ab, läuft alles wunderbar:

Delphi-Quellcode:
myWideChar := PWideChar(myString+'');
Völlig klar: Das Anhängen eines leeren Strings erzeugt einen neuen String der dann nach PWideChar gecastet wird. Und diese Adresse ist schreibbar.



Meine Frage: Ist das hier ein Compilerfehler oder Übereifrige Optimierung? Oder hätte ich glasklar damit rechnen müssen?

jaenicke 12. Nov 2013 13:11

AW: Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Das ist as-designed. Es wird nur eine Referenz benutzt. Du kannst das aber so machen (ungetestet):
Delphi-Quellcode:
const
   defaultStrValue: String = 'Das ist ein String';
var
   myString: String;
   myWideChar: PWideChar;
begin

   myString := defaultStrValue;
   UniqueString(myString);
   myWideChar := PWideChar(myString);
   
   WinApiAufruf(myWideChar); // AV
end;
So musst du das auch bei CreateProcess machen, sonst knallt es bei Unicode, wenn du z.B. einen const Parameter an CreateProcess übergibst.

Der schöne Günther 12. Nov 2013 13:27

AW: Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Ganz genau der Fall (CreateProcess) war es auch ;-)

Ist mir bislang nie aufgefallen da ich die Variable, ausgehend von der Konstante, immer noch einmal verändert hatte.

Fazit: Copy on Write für Delphi-Strings. Bei der Zuweisung
Delphi-Quellcode:
variablerString := konstanterString
hat man natürlich auch erst einmal nur die Adresse der Konstante dort. Muss man aber auch erstmal drauf kommen :roteyes:

DeddyH 12. Nov 2013 13:31

AW: Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Genau dieser Fall ist aber doch beschrieben (unterstes Beispiel):
Zitat:

Aufrufe von CreateProcessW

Die Unicode-Version (CreateProcessW) der Windows-API-Funktion CreateProcess verhält sich etwas anders als die ANSI-Version. Folgendes (sinngemäß übersetzt) findet sich in Bezug auf den lpCommandLine-Parameter in MSDN:

"Die Unicode-Version dieser Funktion, CreateProcessW, kann den Inhalt dieses Strings ändern. Daher darf dieser Parameter kein Zeiger auf schreibgeschützten Speicher sein (wie eine const-Variable oder ein literaler String). Wenn dieser Parameter ein Konstanten-String ist, könnte die Funktion eine Zugriffsverletzung verursachen."

Wegen dieses Problems könnte vorhandener Code, der CreateProcess aufruf, eine Zugriffsverletzung verursachen.

Im Folgenden finden Sie einige Beispiele für solchen problematischen Code:
Delphi-Quellcode:
// Übergabe einer String-Konstanten
CreateProcess(nil, 'foo.exe', nil, nil, False, 0,
  nil, nil, StartupInfo, ProcessInfo);
// Übergabe eines Konstantenausdrucks
  const
    cMyExe = 'foo.exe'
  CreateProcess(nil, cMyExe, nil, nil, False, 0,
    nil, nil, StartupInfo, ProcessInfo);
// Übergabe eines Strings mit dem Referenzzähler -1:
const
  cMyExe = 'foo.exe'
var
  sMyExe: string;
  sMyExe := cMyExe;
  CreateProcess(nil, PChar(sMyExe), nil, nil, False, 0, nil, nil, StartupInfo, ProcessInfo);


Der schöne Günther 12. Nov 2013 13:35

AW: Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Ja, bei MSDN steht das natürlich auch. Das habe ich schon verstanden, ich hatte nur instinktiv damit gerechnet, dass spätestens beim Cast nach
Delphi-Quellcode:
PWideChar
eine neue Kopie vom String angelegt und diese gecastet würde.

DeddyH 12. Nov 2013 13:43

AW: Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Naja,
Zitat:

Delphi-Quellcode:
// Übergabe eines Strings mit dem Referenzzähler -1:

dürfte aber nicht im MSDN stehen ;)

Uwe Raabe 12. Nov 2013 13:49

AW: Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1235538)
Ja, bei MSDN steht das natürlich auch. Das habe ich schon verstanden, ich hatte nur instinktiv damit gerechnet, dass spätestens beim Cast nach
Delphi-Quellcode:
PWideChar
eine neue Kopie vom String angelegt und diese gecastet würde.

Ein simples Beispiel zeigt, daß ein Cast auf PChar eben nur ein Cast ist. Der Delphi-String ist vom Speicher-Layout schon kompatibel zum PChar, so daß der Cast eigentlich nur den Compiler zufrieden stellen soll (nicht nur, ich weiß).

Folgendes Beispiel verdeutlicht das:

Delphi-Quellcode:
  myString := defaultStrValue;
  myString[1] := 'W';
funktioniert.


Delphi-Quellcode:
  myString := defaultStrValue;
  //UniqueString(myString);
  PChar(myString)^ := 'W';
funktioniert nicht ohne das UniqueString.

Der schöne Günther 12. Nov 2013 14:10

AW: Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Delphi-Quellcode:
UniqueString
! Großartig, das ist doch um einiges eleganter als einen leeren String irgendwo anzuhängen oder sich selber was zu basteln.

Danke dafür 8-)

himitsu 12. Nov 2013 14:21

AW: Compiler ersetzt Variable durch Konstante - AV zur Laufzeit
 
Bei dynamischen Arrays gibt es garkein Copy on Write.

Dort wäre eine Art Äquivalent zum UniqueString das Copy.

Delphi-Quellcode:
var
  A, B: array ob Integer;

SetLength(A, 1);
A[0] := 123;
A := Copy(B); // A := B;
B := A;
B[0] := 456;
ShowMessage(IntToStr(A[0]); // 123 und ohne Copy ist es auch 456


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:26 Uhr.

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