![]() |
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 |
Alle Zeitangaben in WEZ +1. Es ist jetzt 20: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