Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010 gesucht (https://www.delphipraxis.net/150635-delphi-7-hack-fuer-assigned-ersatz-fuer-delphi-2010-gesucht.html)

knochen 22. Apr 2010 14:16


Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010 gesu
 
Hallo zusammen.

In unserer Delphi 7 Applikation gibt es eine Funktion, die unter Delphi 7 funktionierte. Möglicherweise ein Hack aus einer Computerzeitschrift oder einem Forum.
Code folgt:

Delphi-Quellcode:
{
Prüft ein Objekt auf einen NIL-Pointer (assigned) und auf
die Gültikeit des Zeigers (SizeOfMem)

Eingabeparameter
    ppeObjRef : zu prüfendes Objekt
Rückgabe
    Boolean --> Objekt-Rferenz gültig oder nicht

}

function ObjektAssigned(ppeObjRef: TObject): Boolean;
type
  PPVmt = ^PVmt;
  PVmt = ^TVmt;
  TVmt = record
    SelfPtr: TClass;
    Other: array[0..17] of Pointer;
  end;
var
  Vmt: PVmt;
begin
  Result := False;
  if Assigned(ppeObjRef) then
  begin
    try
      Vmt := PVmt(ppeObjRef.ClassType);
      Dec(Vmt);
      Result := ppeObjRef.ClassType = Vmt.SelfPtr;
    except
      // Exception ignorieren, Result ist False
    end;
  end;
end;
Die Problematik ist Folgende:
Wenn ein Objekt mit MyObject.Free freigegeben wird, wird von Assigned(MyObject) immer noch True zurückgegeben, obwohl das Objekt schon im Nirwana ist. Obige Funktion gibt in einem solchen Fall False zurück, und das ist genau das gewünschte Verhalten.
Unter Delphi 7 hat das sehr schön funktioniert, aber unter Delphi 2010 knallt's.
Kennt jemand diesen Hack und weiß, wie man ihn auf Delphi 2010 portiert?
Die Funktion FreeAndNil() ist mir bekannt und ich würde sie im äußersten Notfall auch einsetzen, aber wenn es irgend geht dann lieber nicht.

Vielen Dank im Voraus

Bernhard Geyer 22. Apr 2010 14:20

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Zitat:

Zitat von knochen
Die Funktion FreeAndNil() ist mir bekannt und ich würde sie im äußersten Notfall auch einsetzen, aber wenn es irgend geht dann lieber nicht.

Und wieso nicht? Sie wurde genau deshalb eingeführt um keine ungültigen Referenzen mehr übrig zu lassen.

Neutral General 22. Apr 2010 14:24

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Deine Funktion funktioniert bei mir übrigens auch unter Delphi7 nicht:

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var tmp: TObject;
begin
  tmp := TObject.Create;
  tmp.Free;

  if Assigned(tmp) then
    ShowMessage('Assigned nachher!');
  if ObjektAssigned(tmp) then
    ShowMessage('ObjektAssigned nachher!');
end;
Beide MessageBoxen werden angezeigt.

Bernhard Geyer 22. Apr 2010 14:27

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Zitat:

Zitat von Neutral General
Deine Funktion funktioniert bei mir übrigens auch unter Delphi7 nicht:

Evtl. FastMM eingebunden und Debug aktiv? Ich denke solche Hacks funktionieren nicht wenn ein anderer Memory-Manager aktiv ist.

himitsu 22. Apr 2010 14:27

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Tja, es hat sich da halt auch mal was verändert.
(neue RTTI, anderer Speichermanager uwm.)

A: Außerdem waren solche Funktionen eh nie zu 100% zuverläßig

und B: sollte man schon immer in soeinem Fall, wo der Zeiger nach dem Freigeben via Assign geprüft werden soll, FreeAndNil verwenden. Dann ist der Zeiger auch schön NIL.

PS: Wenn zufällig ein ein anders Objekt in der Zwischenzeit den selben Speicherplatz belegt, dann dann würde deise Prüfung auch TRUE liefern, da ja "irgendein" Objekt vorhanden ist.


Zitat:

Beide MessageBoxen werden angezeigt.
tja, wenn der Speicher nicht vom Speichermanager freigegeben wurde und wenn der Speicher nicht überschrieben wurde, dann steht im Speicher immernoch die Signatur, welche hier geprüft wird ... ergo: die Funktion sagt JA.

Hawkeye219 22. Apr 2010 16:12

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Hallo,

eine garantiert zuverlässige Funktion gibt es möglicherweise nicht, eine Alternative für D2010 schon: klick

FreeAndNil ist sicher die bessere Lösung, und auch darauf kann man in den meisten Fällen verzichten, wenn der Code sauber aufgebaut ist. Im EDN gab es dazu mehrere Threads, in denen unter anderem Joanna Carter einige nützliche Hinweise gegeben hat.

Gruß Hawkeye

himitsu 22. Apr 2010 16:29

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Zitat:

Zitat von Hawkeye219
eine garantiert zuverlässige Funktion gibt es möglicherweise nicht, eine Alternative für D2010 schon: klick

Nur ob dieses dann unter Delphi 2012 noch läuft, ist eine andere Sache ... dann doch lieber gleich richtig .Free und FreeAndNil verwenden (jenachdem was benötigt wird).

knochen 23. Apr 2010 07:03

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Was ich mir schon seit ca. 13 Jahren wünsche (Delphi 2), ist ein Compiler- oder IDE-Schalter, der ein Objekt automatisch nilt, wenn ich Free aufrufe. Da werden über die Jahre Features eingebaut, die vor Mächtigkeit nur so strotzen, aber dieses kleine, oft gewünschte und unermesslich nützliche Feature kriegt die Compilercompany nicht hin? - Aua.

So, jetzt zum konstuktiven Teil.
FreeAndNil ist die bessere Alternative, keine Frage!

Zu uns: wir haben ca. 500 MB Quelltexte, historisch gewachsen. Zwei primäre Gründe haben mich zu der Frage veranlasst.
1. Die Destruktoren heißen nicht immer Free.
2. Die Sourcen sind zu ca. einem Viertel generiert.

Auf jeden Fall danke ich euch für eure Vorschläge und Verweise auf andere Threads. Den Vorschlag von Hawkeye219 werde ich auf jeden Fall mal ausprobieren.

schlecki 23. Apr 2010 07:19

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Zitat:

Zitat von knochen
Zu uns: wir haben ca. 500 MB Quelltexte, historisch gewachsen. Zwei primäre Gründe haben mich zu der Frage veranlasst.
1. Die Destruktoren heißen nicht immer Free.

Genau genommen heißen sie niemals Free, sondern _immer_ Destroy ;)

Bernhard Geyer 23. Apr 2010 07:23

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Zitat:

Zitat von knochen
Was ich mir schon seit ca. 13 Jahren wünsche (Delphi 2), ist ein Compiler- oder IDE-Schalter, der ein Objekt automatisch nilt, wenn ich Free aufrufe.

Das gibt es. Nennt sich Delphi.Prism.

Zitat:

Zitat von knochen
Da werden über die Jahre Features eingebaut, die vor Mächtigkeit nur so strotzen, aber dieses kleine, oft gewünschte und unermesslich nützliche Feature kriegt die Compilercompany nicht hin? - Aua.

Also ich habe das nicht vermisste. Dort wo es nötig ist bau ich FreeAndNil ein und dort wo es nicht nötig ist lass ich es so.

Zitat:

Zitat von knochen
Zu uns: wir haben ca. 500 MB Quelltexte, historisch gewachsen. Zwei primäre Gründe haben mich zu der Frage veranlasst.
1. Die Destruktoren heißen nicht immer Free.

Der Destruktor heiß nie Free. Er heißt eigentlich fast immer Destroy! Free ist eh schon ein Hilfskonstrukt mit folgenden Quellcode

Delphi-Quellcode:
  if Self <> nil then
    Destroy;
Zitat:

Zitat von knochen
2. Die Sourcen sind zu ca. einem Viertel generiert.

Das sollte es ja kein Problem sein jeden Free-Aufruf durch FreeAndNil() zu ersetzen.

Zitat:

Zitat von knochen
Auf jeden Fall danke ich euch für eure Vorschläge und Verweise auf andere Threads. Den Vorschlag von Hawkeye219 werde ich auf jeden Fall mal ausprobieren.

Du willst doch nicht den einen problematischen Hack durch den nächsten Ersetzen?

himitsu 23. Apr 2010 07:41

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
.Free wird niemal den Objektzeiger NILen, denn Methoden verändern nie den Objektzeiger und das ist auh gut so.
Einzig und alleine der Constructor macht das indirekt, aber nur weil dafür eine Zuweisung ( := ) verwendet wird.

FreeAndNil greift direkt auf den Objektzeiger (die Variable) zu und kann so deren Inhal ändern.

PS: Wenn jedes Free den Objektzeiger ändern könnte/würde, dann würde es hier Probleme geben.
Delphi-Quellcode:
procedure Test(const Obj: TObject);
begin
  obj.Free;
end;
Zitat:

Zitat von knochen
Was ich mir schon seit ca. 13 Jahren wünsche (Delphi 2), ist ein Compiler- oder IDE-Schalter, der ein Objekt automatisch nilt, wenn ich Free aufrufe. Da werden über die Jahre Features eingebaut, die vor Mächtigkeit nur so strotzen, aber dieses kleine, oft gewünschte und unermesslich nützliche Feature kriegt die Compilercompany nicht hin? - Aua.

Also um es nochmals zu sagen:
- .Free ist eine Methode
- Methoden ändern niemal Objektvariablen
- und ich weiß nicht warum sich jemand sowas wirklich wünschen würde?

Immerhin gibt es hierfür FreeAndNil und da weiß man genau was es macht.
.Free gibt nur das Objekt frei und macht sonst nichts.

knochen 23. Apr 2010 08:25

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Ich möchte die Diskussion nicht ausufern lassen, aber kurz noch Folgendes:

Zitat:

Zitat von himitsu
Wenn jedes Free den Objektzeiger ändern könnte/würde, dann würde es hier Probleme geben.
Delphi-Quellcode:
procedure Test(const Obj: TObject);
begin
  obj.Free;
end;

Das würde dann wohl der Compiler verbieten, genauso wie FreeAndNil in obiger Methode.

Zitat:

Zitat von himitsu
- .Free ist eine Methode
- Methoden ändern niemal Objektvariablen
- und ich weiß nicht warum sich jemand sowas wirklich wünschen würde?

Weil es Probleme lösen würde! Abgesehen davon: wenn es sich keiner wünscht, warum wurde es dann in Prism so gemacht?
BTW: Ich kann mich an eine Sprache namens Clipper erinnern, mit der man vor 20 Jahren so etwas per Preprozessordirektive leicht lösen konnte.


Zitat:

Zitat von himitsu
Immerhin gibt es hierfür FreeAndNil und da weiß man genau was es macht.
.Free gibt nur das Objekt frei und macht sonst nichts.

Schlimm genung, denn das ist ja das Problem...

generic 23. Apr 2010 08:28

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Prüft nicht Assigned intern nur auf NIL?

Denn einen Zeiger mit einer Adresse im Bauch, kann man nicht ansehen ob der Zeiger noch gültig ist oder nicht.
Daher die NIL Geschichte.

himitsu 23. Apr 2010 09:30

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Zitat:

Zitat von knochen
Das würde dann wohl der Compiler verbieten,

Genau. Und jetzt stell dir mal vor das Free würde standardmäßig den Objektzeiger auf NIL setzen.
Schon könnte man hier das Objekt nicht mehr "ordentlich" freigeben. :stupid:

Egal ob man es über einen Compilerschalter an- oder ausstellen kann ... sowas wird ja global gesetzt und ein lokaler Compilerschalter ist Schwachsinn, denn da kann man auch gleich FreeAndNil verwenden, welches ich dann übersichtlicher/verständlicher finde.

PS: C# ist was anderes, denn dort arbeitet auch ein GarbageCollector und es gibt insgesamt einen anderes Speichermanagement.
Dort gibt es quasi keine Objekte, sondern eigentlich nur sowas wie Interfaces und der Speichermanager hat die volle Kontrolle über die ganzen Zeiger, bzw. er muß sogar die Kontrolle haben, sonst geht sowas garnicht.


Zitat:

Zitat von generic
Prüft nicht Assigned intern nur auf NIL?

Genau.

Wie gesagt, wenn man auf die interne Struktur schaut, dann kann das nix werden, denn
- entweder liegt an der Stelle irgendwas, welches zufällig so aussueht, wie das auf welches man prüfen will
- oder es liegt inzwischen ein anderes Objekt dort. (siehe nachfolgender Code)

Bsp:
Delphi-Quellcode:
var ObjA, ObjB: TMyObject;

ObjA := TMyObject.Create;
ObjA.Free;
ObjB := TMyObject.Create;

if ObjektAssigned(ObjA) then
  // wenn jetzt zufällig der Speicher des Objekts B an der selben Stelle liegt,
  // wo vorher das Objekt A lag, dann würde ObjektAssigned an dieser Stelle
  // natürlich ein Objekt vorfinden (und zwar das aus ObjB) und würde demnach
  // korrekter Weise TRUE liefern, obwohl in ObjA eigentlich kein Objekt mehr drin ist.






Delphi-Quellcode:
program Project3;

uses
  Windows;

type
  TString20 = String[20];
  TStrObject = class
  private
    FStr: TString20;
  public
    constructor Create(const S: TString20);
    property Str: TString20 read FStr;
  End;

constructor TStrObject.Create(const S: TString20);
begin
  FillChar(FStr, SizeOf(FStr), 0);
  FStr := S;
end;

var
  ObjA, ObjB: TStrObject;

begin
  ObjA := TStrObject.Create('Ich bin A.');
  ObjA.Free;
  ObjB := TStrObject.Create('Ich aber bin B?');
  MessageBoxA(0, PAnsiChar(@ObjA.Str[1]), nil, 0);
  if ObjA = ObjB then ;
end.
ObjA sagt:
Ich aber bin B?


Delphi-Quellcode:
program Project3;

uses
  Windows;

type
  TString20 = String[20];
  TStrObject = class
  private
    FStr: TString20;
  public
    constructor Create(const S: TString20);
    property Str: TString20 read FStr;
  End;

constructor TStrObject.Create(const S: TString20);
begin
  FillChar(FStr, SizeOf(FStr), 0); // nur wegen der #0 hinter dem String
  FStr := S;
end;

var
  ObjA, ObjB: TStrObject;

begin
  ObjA := TStrObject.Create('Ich bin A.');
  FreeAndNil(ObjA);
  ObjB := TStrObject.Create('Ich bin B.');
  if Assigned(ObjA) then
    MessageBoxA(0, PAnsiChar(@ObjA.Str[1]), nil, 0)
  else
    MessageBoxA(0, 'ObjA existiert nicht.', nil, 0);
  if ObjA = ObjB then ; // für's Debugging
end.
ObjA sagt:
ObjA existiert nicht.


Delphi-Quellcode:
program Project3;

uses
  Windows, SysUtils;

type
  TString20 = String[20];
  TStrObject = class
  private
    FStr: TString20;
  public
    constructor Create(const S: TString20);
    property Str: TString20 read FStr;
  End;

constructor TStrObject.Create(const S: TString20);
begin
  FillChar(FStr, SizeOf(FStr), 0); // nur wegen der #0 hinter dem String
  FStr := S;
end;

function IsValidObject(aObject: TObject): Boolean;
type
  PVmt = ^TVmt;
  TVmt = packed record
    SelfPtr: TClass;
    ignored: array [0..-(4 + vmtSelfPtr) - 1] of Byte;
  end;
var
  VMT: PVmt;
begin
  Result := False;
  if IsBadReadPtr(aObject, 4) then
    Exit;
  VMT := PVmt(aObject.ClassType);
  Dec(VMT);
  if IsBadReadPtr(VMT, 4) then
    Exit;
  if aObject.ClassType = VMT.SelfPtr then
    Result := True;
end;

var
  ObjA, ObjB: TStrObject;

begin
  ObjA := TStrObject.Create('Ich bin A.');
  ObjA.Free;
  ObjB := TStrObject.Create('Ha, ich bin B.');
  if IsValidObject(ObjA) then
    MessageBoxA(0, PAnsiChar(@ObjA.Str[1]), nil, 0)
  else
    MessageBoxA(0, 'ObjA existiert nicht.', nil, 0);
  if ObjA = ObjB then ; // für's Debugging
end.
ObjA sagt:
Ha, ich bin B.


Delphi-Quellcode:
program Project3;

uses
  Windows, SysUtils;

type
  TString30 = String[30];
  TStrObject = class
  private
    FStr: TString30;
  public
    constructor Create(const S: TString30);
    property Str: TString30 read FStr;
  End;

constructor TStrObject.Create(const S: TString30);
begin
  FillChar(FStr, SizeOf(FStr), 0); // nur wegen der #0 hinter dem String
  FStr := S;
end;

function IsValidObject(aObject: TObject): Boolean;
type
  PVmt = ^TVmt;
  TVmt = packed record
    SelfPtr: TClass;
    ignored: array [0..-(4 + vmtSelfPtr) - 1] of Byte;
  end;
var
  VMT: PVmt;
begin
  Result := False;
  if IsBadReadPtr(aObject, 4) then
    Exit;
  VMT := PVmt(aObject.ClassType);
  Dec(VMT);
  if IsBadReadPtr(VMT, 4) then
    Exit;
  if aObject.ClassType = VMT.SelfPtr then
    Result := True;
end;

var
  ObjA: TStrObject;

begin
  ObjA := TStrObject.Create('Huch, was ist denn das hier?');
  ObjA.Free;
  if IsValidObject(ObjA) then
    MessageBoxA(0, PAnsiChar(@ObjA.Str[1]), nil, 0)
  else
    MessageBoxA(0, 'ObjA existiert nicht.', nil, 0);
  if ObjA = nil then ; // für's Debugging
end.
ObjA sagt:
Huch, was ist denn das hier?


Delphi-Quellcode:
program Project3;

uses
  Windows, SysUtils;

type
  TString20 = String[20];
  TStrObject = class
  private
    FStr: TString20;
  public
    constructor Create(const S: TString20);
    property Str: TString20 read FStr;
  End;

constructor TStrObject.Create(const S: TString20);
begin
  FillChar(FStr, SizeOf(FStr), 0); // nur wegen der #0 hinter dem String
  FStr := S;
end;

var
  ObjA, ObjB: TStrObject;

begin
  ObjA := TStrObject.Create('Ich bin A.');
  FreeAndNil(ObjA);
  ObjB := TStrObject.Create('Ich bin B.');
  if Assigned(ObjA) then
    MessageBoxA(0, PAnsiChar(@ObjA.Str[1]), nil, 0)
  else
    MessageBoxA(0, 'Ich existiere nicht.', nil, 0);
  if ObjA = ObjB then ; // für's Debugging
end.
ObjA sagt:
Ich existiere nicht.



Zitat:

Zitat von knochen
Schlimm genung, denn das ist ja das Problem...

Ich sehe da kein Problem.
In Delphi liegt die Kontrole über den Speicher beim Programmierer (abgrsehn einige bestimmter Typen, wie Strings und dyn. Arrays) und via .Free und FreeAndNil hat er die Kontrolle darüber was gemacht werden soll ...... er muß es nur ordentlich nutzen.

Medium 23. Apr 2010 09:34

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Zwar leicht OT, aber das ist einer der Gründe, warum ich .NET mittlerweile voll gerne mag. Foo = null; und der GC kümmert sich um den Rest, heile Welt ohne Krämpfe.

Ich vermute auch, dass der Schwenk hin zu managed Umgebungen seinen Teil dazu beiträgt, dass dieser Problematik bei den IDE/Sprach-Entwicklern weithin eher wenig Bedeutung mehr angerechnet wird.

(Davon ab halte ich hier auch FreeAndNil() für den geeignetesten Weg, der auch eigentlich immer klappen sollte. Es sei denn, ihr verhackstückt da regelrecht kriminelle Dinge, und dann ist's eh wurscht :P)

himitsu 23. Apr 2010 09:46

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Zitat:

Zitat von Medium
Foo = null; und der GC kümmert sich um den Rest, heile Welt ohne Krämpfe.

Erstell dir ein Interface und schon geht das auch in Delphi. :zwinker:

Medium 23. Apr 2010 09:59

Re: Delphi 7 Hack für Assigned -> Ersatz für Delphi 2010
 
Wer tut das schon? :angel2: (Ich, zumindest mit diesem als alleinigen Grund, sicherlich nicht. Dafür ist mir FreeAndNil() im Vergleich wieder zu einfach anwendbar :stupid: )


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:59 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