![]() |
Warum zerstören sich meine Classen
Ich bekomme langsam die Krise.
Ich hab ein neues Control angefangen uGrid..
Delphi-Quellcode:
sollte alles in Ordnung sein.
type
ISkinGrid = interface ['{89A97429-5E4B-43B6-87D8-381DD4E8CF21}'] function GetHandle: hWnd; property Handle: hWnd read GetHandle; end; TSkinGrid = class(TInterfacedObject, ISkinGrid) private FHGrid: HWND; FWidth: Integer; FHeight: Integer; Img: cardinal; dwStyle: DWORD; IsInitialized: BOOL; FOffsetX: Integer; FOffsetY: Integer; function GetOffsetY: Integer; function GetOffsetX: Integer; function GetHandle: hWnd; procedure DrawGrid(WinHandle: HWND); public property Handle: HWND Read FhGrid; property Width: Integer Read FWidth; property Height: Integer Read FHeight; property OffsetX: Integer read GetOffsetX write FOffsetX; property OffsetY: Integer read GetOffsetY write FOffsetY; constructor Create(hOwner: HWND; FullpathImageName: string; x, y, xW, yH, OffsX, OffsY, ButID: Integer); destructor Destroy; override; end; Classe wird ordnungsgemäß erstellt. Danach stimmen alles meine Variablen. Mit einmal zerstört sie sich selbst und alles ist weg. Warum? Der Aufruf wie zuvor.
Delphi-Quellcode:
BoxGrid := CTRL_GridCreate(hMain, SKAERO_FOLDER +
'Grid.png', 150, 67, 585, 257, 20, 10, ID_BOXGRID); SKAERO_SetAnchorMode(BoxGrid.Handle, ANCHOR_HEIGHT_WIDTH); SKAERO_SetZorder(BoxGrid.Handle, HWND_TOP); Siehe Bilder.. Wie man sehen kann sind bei DrawGrid alle Eigenschaften gelöscht. gruss |
AW: Warum zerstören sich meine Classen
Interfaces lösen sich selbst auf, sobald nicht mehr auf sie zugegriffen wird (Referenz-Zähler = 0). Hast du eventuell eine gleichnamige lokale Variable zu deiner eventuell vorhandenen globalen Variable?
Bernhard |
AW: Warum zerstören sich meine Classen
Du musst bei Interfaces die Referenzzählung beachten. Wenn der Referenzzähler 0 erreicht, wird das Objekt automatisch zerstört.
Das kann z.B. passieren, wenn du Klassen- und Interface-Variablen mischt:
Delphi-Quellcode:
Dabei muss nicht unbedingt eine Interface-Variable auf nil gesetzt werden, es kann auch sein, dass in einer Subroutine ein Objekt einer lokalen Interface-Variable zugewiesen wird. Am Ende der Subroutine wird die Variable nämlich finalisiert, der Referenzzähler wird also ebenfalls dekrementiert, natürlich mit dem selben Effekt.
type
IMyObj = interface … end; TMyObj = class(TInterfacedObject, IMyObj) … end; var obj: TMyObj; intf: IMyObj; begin obj := TMyObj.Create; // Referenzzähler von obj ist 0 intf := obj; // Referenzzähler von obj wird inkrementiert → 1 intf := nil; // Referenzzähler von obj wird dekrementiert → 0 → obj wird freigegeben { ab hier kannst du obj nicht mehr benutzen } end Deshalb sollte man Interfaces wenn dann immer konsequent verwenden. |
AW: Warum zerstören sich meine Classen
Hier ist mal meine komplette Klasse..
Hab nicht gedacht das es so schwierig mit den Interfacen ist.
Delphi-Quellcode:
unit uGrid;
interface uses Windows, Classes, Messages, SysUtils, uGlobal, uGDIUnit, uSkin, uTrackBar; type ISkinGrid = interface ['{89A97429-5E4B-43B6-87D8-381DD4E8CF21}'] function GetHandle: hWnd; property Handle: hWnd read GetHandle; end; TSkinGrid = class(TInterfacedObject, ISkinGrid) private FHGrid: HWND; FWidth: Integer; FHeight: Integer; Img: cardinal; dwStyle: DWORD; IsInitialized: BOOL; FOffsetX: Integer; FOffsetY: Integer; function GetOffsetY: Integer; function GetOffsetX: Integer; function GetHandle: hWnd; procedure DrawGrid(WinHandle: HWND); public property Handle: HWND Read FhGrid; property Width: Integer Read FWidth; property Height: Integer Read FHeight; property OffsetX: Integer read GetOffsetX write FOffsetX; property OffsetY: Integer read GetOffsetY write FOffsetY; constructor Create(hOwner: HWND; FullpathImageName: string; x, y, xW, yH, OffsX, OffsY, ButID: Integer); destructor Destroy; override; end; function GridProc(WinHandle: HWND; Msg: UINT; wP: WParam; lP: LParam): LRESULT; stdcall; var SkinGrid : TSkinGrid; implementation constructor TSkinGrid.Create(hOwner: HWND; FullpathImageName: string; x, y, xW, yH, OffsX, OffsY, ButID: integer); var wc: TWndClassEx; zClass: PAnsiChar; begin inherited Create; with SkinEngine do begin zClass := SKGRID; wc.cbSize := SIZEOF(wc); IsInitialized := GetClassInfoEx(skInstance, zClass, wc); if IsInitialized = False then begin wc.cbSize := SIZEOF(wc); wc.style := CS_HREDRAW or CS_VREDRAW or CS_DBLCLKS;{ or CS_PARENTDC;} wc.lpfnWndProc := @GridProc; wc.cbClsExtra := 0; wc.cbWndExtra := EXTEND_EXTRA * 4; wc.hInstance := skInstance; wc.hIcon := 0; wc.hCursor := 0; wc.hbrBackground := 0; wc.lpszMenuName := nil; wc.lpszClassName := zClass; wc.hIconSm := wc.hIcon; if RegisterClassEx(wc) <> 0 then IsInitialized := True; end; if IsInitialized = True then begin dwStyle := WS_CHILD or WS_VISIBLE or WS_TABSTOP; // Erstelle das GDIPLUS image von Datei Img := AddResource(PAnsiChar(FullpathImageName)); if Img <> 0 then begin // Hole die Thumb GDIPLUS image größe GetImageSize(Img, imgW, imgH); FWidth := xW; FHeight := yH; FOffsetX := OffsX; FOffsetY := OffsY; FHGrid := CreateWindowEx(WS_EX_TRANSPARENT, SKGRID, nil, dwStyle, x, y, xW, yH, hOwner, ButID, skInstance, nil); if FHGrid <> 0 then begin // Speichere das Image Handle in die Property SetImageProperty(FHGrid, PROP_STYLE, BS_GROUPBOX); SetImageProperty(FHGrid, GRID_IMAGE, Img); SkinGrid := @FHGrid; SkinGrid.Img := Img; SkinGrid.dwStyle := dwStyle; SkinGrid.FWidth := xW; SkinGrid.FHeight := yH; SkinGrid.FOffsetX := OffsX; SkinGrid.FOffsetY := OffsY; end else // Image löschen wenn Fehler DeleteResource(Img); end; end; end; end; function GridProc(WinHandle: HWND; Msg: UINT; wP: WParam; lP: LParam): LRESULT; var ps: TPaintstruct; begin with SkinEngine do begin case Msg of WM_ERASEBKGND: begin Result := 1; exit; end; WM_DESTROY: begin PostQuitMessage(0); Result := 0; Exit; end; WM_PAINT: begin BeginPaint(WinHandle, ps); SkinGrid.DrawGrid(WinHandle); EndPaint(WinHandle, ps); Result := 0; Exit; end; end; Result := DefWindowProc(WinHandle, Msg, wP, lP); end; // end SkinEngine end; destructor TSkinGrid.Destroy; begin inherited Destroy; end; procedure TSkinGrid.DrawGrid(WinHandle: HWND); var graphics: Cardinal; pen: Integer; IntI: Integer; DC: HDC; rc: TRect; begin with SkinEngine do begin // Initialisierern DC := GetDC(WinHandle); GetClientRect(WinHandle, rc); GdipCreateFromHDC(DC, graphics); GdipCreatePen1(ColorARGB(255, RGB(0, 0, 0)), 1, UnitPixel, pen); for IntI := 0 to rc.Right do begin if IntI mod FOffsetX <> 0 then continue; GdipDrawLineI(graphics, pen, IntI, 0, IntI, rc.Bottom); end; for IntI := 0 to rc.Bottom do begin if IntI mod FOffsetY <> 0 then continue; GdipDrawLineI(graphics, pen, 0, IntI, rc.Right, IntI); end; // Freigeben GdipDeletePen(pen); GdipDeleteGraphics(graphics); ReleaseDC(WinHandle, DC); end; end; function TSkinGrid.GetHandle: hWnd; begin result := FhGrid; end; function TSkinGrid.GetOffsetX: Integer; begin Result := FOffsetX; end; function TSkinGrid.GetOffsetY: Integer; begin Result := FOffsetY; end; end.
Delphi-Quellcode:
SkinGrid := @FHGrid;
Mußte ich auf die Classe casten weil ich sonst ein problem mit den Variablen in der WinProc habe. Zitat:
Aber der zähler ist auf 0 .. kann man auf dem bild auch sehen. EDIT: Wenn ich jetzt eine globale Variable definiere. Zitat:
dann funktioniert es. Zitat:
Nur welchen Sinn haben dann die Variablen die ich in der Classe definiert habe wenn sie nicht funktionieren. Bzw.. das Interface vorher zerstört wird. gruss |
AW: Warum zerstören sich meine Classen
Definiere doch mal (und wenn auch nur zum Testen) folgende Variable:
Delphi-Quellcode:
Dann erzeugst du das SkinGrid mit
var
FPrevClientProc: TFNWndProc; SkinGrid : TSkinGrid; SkinGridIntf: ISkinGrid;
Delphi-Quellcode:
und schau mal, ob es dann geht.
SkinGridIntf := TSkinGrid.Create(...);
Sorry, aber deine Art mit Klassen, Interfaces und Handles zu hantieren ist irgendwie, sagen wir "unüblich". Wenn du die Referenzzählung nicht brauchst, dann leite deine Klassen nicht von TInterfacedObject sonder von TInterfacedPersistent (classes.pas) ab. Dann musst du die Instanzen aber auch selbst wieder freigeben. |
AW: Warum zerstören sich meine Classen
Diese Variablen werden überschrieben, sobald es mehr als eine Instance von TSkinGrid gibt:
Delphi-Quellcode:
Deshalb müssen diese Member der Klasse sein.
var
FPrevClientProc: TFNWndProc; SkinGrid : TSkinGrid; In der Funktion GridProc soll auf die jeweilige Instanz von TSkinGrid zugegriffen werden. Benötigt wird eine List aller Instanzen, um sich auf Grund des Handle die richtige zu ermitteln. Das Interface und die implementierende Klasse in der selben Unit zu deklarieren ist zumindest fragwürdig. Aus welchen Gründen wird hier überhaupt mit einem Interface gearbeitet? Ich würde TSkinGrid von TComponent ableiten und beim Constructor als Owner die jeweils das skinnende Objekt übergeben. Damit hat das TSkinGrid die selbe Lebensdauer wie der Owner und es kann auch auf Owner zugegriffen werden. |
AW: Warum zerstören sich meine Classen
Ich nehme an er nimmt interfaces um später mal Skins aus DLLs importieren zu können. ???
|
AW: Warum zerstören sich meine Classen
Delphi-Quellcode:
Aus welchen Gründen wird hier überhaupt mit einem Interface gearbeitet?
Weil meine Objecte(Controls oder wie auch immer) sich in einer DLL befinden. Und ich von außen auf die später Mitgelieferte API.. der DLL zugreife. Wie also soll ich das komplette Object von außen Aufrufen ohne Interface?
Delphi-Quellcode:
Das ist in der Anwendung...
BoxGrid := CTRL_GridCreate(hMain, SKAERO_FOLDER +
'Grid.png', 150, 67, 585, 257, 20, 10, ID_BOXGRID); SKAERO_SetAnchorMode(BoxGrid.Handle, ANCHOR_HEIGHT_WIDTH); SKAERO_SetZorder(BoxGrid.Handle, HWND_TOP); Der rest in einer DLL Warum soll der User der meine DLL verwenden möchte sich mit GDI+ und konsorte rumquälen wenn die DLL das später alles zur verfügung stellt.
Delphi-Quellcode:
function CTRL_GridCreate(hOwner: HWND; FullpathImageName: string;
x, y, xW, yH, OffsX, OffsY, ButID: Integer): ISkinGrid; stdcall; begin result := TSkinGrid.Create(hOwner, FullpathImageName, x, y, xW, yH, OffsX, OffsY, ButID); end; Zitat:
Habe ich wieder entfernt wollte die GridProc Subclassen ist aber in dem Fall nicht nötig. gruss |
AW: Warum zerstören sich meine Classen
Es hängt ja nicht daran, wo was ist und für was es verwendet werden soll, sondern wie die Typen gebraucht werden. Für ein Interface gibt es einen automatischen Referenzzähler (also ein Garbage Collector für Delphi, der funktioniert). Dieser funktioniert allerdings nur dann korrekt, wenn man keine Objekt-Typen mehr verwendet, sondern nur noch Interface-Typen.
2. Stoplerstein: Wenn ein Interface keiner Variablen mehr zugeordnet ist, dann heißt das automatisch, dass das Interface weg muss (Garbage Collector). Dazu im Gegensatz das Nicht-Interface-Objekt: Das verschwindet erst, wenn jemand explizit Free (Destroy) aufgerufen hat. Bernhard PS: Hoffe, alles nochmal zusammengefasst und richtig dargestellt zu haben. |
AW: Warum zerstören sich meine Classen
Zitat:
Wie Uwe schon sagt... Zitat:
Er hat schon recht aber letztendlich zählt das ergebnis wie man dahin kommt ist eine andere sache. Ich verwende jetzt TInterfacedPersistent obwohl mir der unterschied nicht geläufig ist abgesehen davon das sie keinen Referenzzähler hat Damit funktioniert es. Wie muß ich das nun korrekt freigeben? Bedeutet das ich müßte eine Free Methode nach außen weiterleiten? gruss |
AW: Warum zerstören sich meine Classen
Der GC macht ja nichts anderes als .Free aufzurufen, wenn keine Instanzen mehr vorhanden sind. Wenn du jetzt den Referenzzähler nicht benutzt, brauchst du eine Methode, die zumindest intern .Free aufruft. Du kannst aber auch das Free über dein Interface nach außen geben.
Bernhard |
AW: Warum zerstören sich meine Classen
Zitat:
Mit TInterfacedPersistent wird jetzt kein Destroy mehr aufgerufen. Wie muss die Funktion Free also aussehen? gruss |
AW: Warum zerstören sich meine Classen
.Free ist eine Methode deines OBJEKTES und diese Methode existiert weiterhin. Da sie in TObject definiert worden ist, sollte es kein Problem sein, diese zu verwenden.
Was sie macht: Nach einer Prüfung, ob das Objekt überhaupt existiert, wird Destroy veranlasst. Bernhard |
AW: Warum zerstören sich meine Classen
Zitat:
Deshalb auch meine Frage. gruss |
AW: Warum zerstören sich meine Classen
Natürlich hast du immer ein Objekt, da man Klassen nur ableiten kann und die Grundklasse, die immer dabei ist ist TObjekt.
Du kannst dir das wie eine Hierarchie vorstellen und an der Spitze steht immer TObject. Bernhard |
AW: Warum zerstören sich meine Classen
Zitat:
Das wollte ich gerne wissen. Wenn ich Free nach außen weiterleite muss die Anwendung doch wissen was sie damit tun soll.. Bzw auf was sie verzweigt. gruss |
AW: Warum zerstören sich meine Classen
Falsche Richtung.
Delphi-Quellcode:
So war das gemeint. Du sollst nicht das Objekt irgendwo hin geben, sondern wo anders die Möglichkeit anbieten, das Objekt zu löschen.
type
IMyInterface = interface(IUnknown) public // ... procedure Free; end; Bernhard |
AW: Warum zerstören sich meine Classen
Zitat:
Es reicht in meiner Classe ein einfaches procedure Free zu deklarieren? Und die ruft dann Tobject vom Interface TInterfacedPersistent auf.
Delphi-Quellcode:
type
ISkinGrid = interface ['{89A97429-5E4B-43B6-87D8-381DD4E8CF21}'] function GetHandle: hWnd; property Handle: hWnd read GetHandle; procedure Free; end; Hmm etwas unverständlich. Ja ich lerne es noch mit den Interfacen. EDIT: Es funktioniert auf jedenfall. Danke für eure Hilfe! gruss |
AW: Warum zerstören sich meine Classen
So grob stimmt das. Du definierst ein Free in deinem Interface und schon ist es zugänglich. Free ist bei deinem Objekt schon definiert und zieht mit deinem TInterfacedPersistent ein (passiert auch bei TInterfacedObject). Free sorgt dafür, dass das Objekt verschwindet. Heißt also: Von außen Free aufrufen und weg ist das Objekt.
Bernhard ADD: Eben so, als würdest du das Objekt vor dir haben. |
AW: Warum zerstören sich meine Classen
Zitat:
Überlege nun ob ich alle Interface nach TInterfacedPersistent umlege. Das verhindert zumindest das sie selbst zerstört werden. In wie weit sich das jetzt auf Speicher usw.. bemerkbar macht weiss ich allerdings nicht. Denke da könnte einiges zusammen kommen wenn diese während der laufzeit nicht freigegeben werden. Da ist eure Meinung gefragt. :) gruss |
AW: Warum zerstören sich meine Classen
Zitat:
Bernhard |
AW: Warum zerstören sich meine Classen
Boa, seid ihr alle gruselig... :pale::pale:
1) Wer versucht Free in ein Interface aufzunehmen hat die zugrundeliegende Problematik von Interfaces in Delphi nicht verstanden - sowas führt auf direktem Weg zu einer AV: 6 - setzen! Prüft mal eure Programme auf Zugriffe auf freigegebenen Speicher... 2) Objekte mit Interfaces zerstören sich nicht selber wenn der RefCount 0 wird. Es wird lediglich _Release() aufgerufen. Nur wenn man von einer Klasse ableitet, die in _Release() den Destructor aufruft (z.B. TInterfacedObject) zerstört sich das Object bei RefCount 0. 3) Objekte freigeben, auf die es noch irgendwelche Interface-Referenzen gibt ist *VERBOTEN*. Tut man es trotzdem, wird später wenn die Interface-Referenz gelöscht wird das _Release() des bereits freigegebenen Objektes aufgerufen. Zur Lösung des Problems: Vermutlich hilft hier eine Wrapper-Klasse, die sich um die Erstellung und das Aufräumen kümmert. Z.B. wäre es auch ganz nett die zugehörige Arbeit zum Erstellen und Aufräumen innerhalb der DLL in eigene Init und Exit Prozeduren (ohne Interfaces) auszulagern. |
AW: Warum zerstören sich meine Classen
Ich sagte doch: Interfaces richtig benutzen. Das hier ist ein Quick&Dirty, vor allem Dirty, Workaround, der es dem TE erleichtern soll, das ganze zu managen, weil scheinbar immernoch ein paar Probleme mit Interfaces und deren Benutzung existieren.
Bernhard |
AW: Warum zerstören sich meine Classen
Zitat:
Der TE ist kein professioneller Programmierer und schon gar nicht Informatiker. Berücksichtige das Bitte! Ich stelle nur Fragen wenn Sie beantwortet werden freut es mich natürlich. Soll aber nicht heißen das jemand für mein Hobby (Arbeiten soll). Es ist der Sinn eines Forums das man sich dort informiert um zu lernen. Und was an meiner letzten Frage jetzt so unverständlich war bzw.. was es damit zu tun hat es mir leichter zu machen verstehe ich jetzt nun wirklich nicht. Ich habe gefragt ob sich das ändern der Interface auf den Speicher auswirkt. Was war da nun so tragisch :?: gruss |
AW: Warum zerstören sich meine Classen
das soll jetzt aber nicht zum persönlichen Angriff mutieren. Außerdem hatte ich gedacht, dass die bis jetzt erarbeitete Lösung so halbwegs funktioniert und deinen Ansprüchen fürs erste genügt. Ich habe mich noch nie damit beschäftigt, wie man Interfaces einsetzt um ganze Objekte zwischen DLL und EXE auszutauschen, ich kann also nur Tipps geben, wie und ob das funktioniert, kann ich nicht 100%ig sagen.
Bernhard PS: Es klingt nur für mich komisch, dass die zuerst angesprochene Version nicht funktioniert, wobei die gerade das Non-Plus-Ultra ist. |
AW: Warum zerstören sich meine Classen
Zitat:
Das war kein Angriff auf deine Person. Danke nochmal für deine Hilfe. PS: Möchte halt nur verstehen wie das alles zusammenhängt. Wo soll ich denn fragen wenn nicht hier ;) gruss |
AW: Warum zerstören sich meine Classen
Free in das Interface aufzunehmen oder das Objekt über eine andere Methode des Interface freizugeben ist problematisch. Es existiert zu diesem Zeitpunkt zumindest diese eine Interfacevariable. Danach wird irgendwann der Gültigkeitsbereich für diese Variable verlassen, das übergeordnete Objekt freigegeben, bzw. der Interface-Variable NIL oder ein anderer Wert zugewiesen.
Dabei wird automatisch über das Interface "_Release" des implementierenden Objects aufgerufen.
Delphi-Quellcode:
Da der versteckt übergebene Parameter Self auf ein nicht mehr existierendes Objekt verweist, kann der implizite Zugriff auf Self.FOwnerInterface zur Zugriffsverletzung führen.
function TInterfacedPersistent._Release: Integer;
begin if FOwnerInterface <> nil then Result := FOwnerInterface._Release else Result := -1; end; |
AW: Warum zerstören sich meine Classen
Zitat:
Denke das Windows das Automatisch übernimmt wenn das Programm geschlossen wird. gruss |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:15 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