Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Warum zerstören sich meine Classen (https://www.delphipraxis.net/159283-warum-zerstoeren-sich-meine-classen.html)

EWeiss 21. Mär 2011 22:54

Warum zerstören sich meine Classen
 
Ich bekomme langsam die Krise.
Ich hab ein neues Control angefangen uGrid..

Delphi-Quellcode:
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;
sollte alles in Ordnung sein.

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

rollstuhlfahrer 21. Mär 2011 23:05

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

Namenloser 21. Mär 2011 23:08

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:
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
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.

Deshalb sollte man Interfaces wenn dann immer konsequent verwenden.

EWeiss 21. Mär 2011 23:34

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:

Hast du eventuell eine gleichnamige lokale Variable zu deiner eventuell vorhandenen globalen Variable?
Denke nicht.
Aber der zähler ist auf 0 .. kann man auf dem bild auch sehen.

EDIT:
Wenn ich jetzt eine globale Variable definiere.
Zitat:

mOffsetX : Integer;
mOffsetY : Integer;
und übergebe dieser die Variable welche noch gültig ist beim start
dann funktioniert es.

Zitat:

mOffsetX := FOffsetX;
mOffsetY := FOffsetY;

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

Uwe Raabe 22. Mär 2011 07:14

AW: Warum zerstören sich meine Classen
 
Definiere doch mal (und wenn auch nur zum Testen) folgende Variable:

Delphi-Quellcode:
var
  FPrevClientProc: TFNWndProc;
  SkinGrid : TSkinGrid;
  SkinGridIntf: ISkinGrid;
Dann erzeugst du das SkinGrid mit

Delphi-Quellcode:
SkinGridIntf := TSkinGrid.Create(...);
und schau mal, ob es dann geht.

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.

Blup 22. Mär 2011 08:41

AW: Warum zerstören sich meine Classen
 
Diese Variablen werden überschrieben, sobald es mehr als eine Instance von TSkinGrid gibt:
Delphi-Quellcode:
var
  FPrevClientProc: TFNWndProc;
  SkinGrid : TSkinGrid;
Deshalb müssen diese Member der Klasse sein.
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.

QuickAndDirty 22. Mär 2011 11:13

AW: Warum zerstören sich meine Classen
 
Ich nehme an er nimmt interfaces um später mal Skins aus DLLs importieren zu können. ???

EWeiss 22. Mär 2011 11:36

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:
    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);
Das ist in der Anwendung...
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:

FPrevClientProc: TFNWndProc;

Habe ich wieder entfernt wollte die GridProc Subclassen
ist aber in dem Fall nicht nötig.

gruss

rollstuhlfahrer 22. Mär 2011 11:43

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.

EWeiss 22. Mär 2011 11:58

AW: Warum zerstören sich meine Classen
 
Zitat:

Zitat von rollstuhlfahrer (Beitrag 1090234)
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.

Danke das habe ich jetzt verstanden.

Wie Uwe schon sagt...
Zitat:

Sorry, aber deine Art mit Klassen, Interfaces und Handles zu hantieren ist irgendwie, sagen wir "unüblich".

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 09:58 Uhr.
Seite 1 von 3  1 23      

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