Metoda + SetTimer = błędne wartości zmienny

Odpowiedz Nowy wątek
Taito
2012-11-02 20:31
Taito
0

Witam!

A więc mam dość kłopotliwy problem mianowicie: mam klasę w której tworzę timer przez SetTimer WinAPI.

SetTimer(0, ID, 50, Addr(TMyClass.TimerProc));

I tu się zaczynają schody... Po utworzeniu klasy i wywołaniu procedury która odpala ten timer zmienne prywatne klasy maja wartości z kosmosu... Wszystko jest tworzone dynamicznie z dala od klas typu TForm i niech tak zostanie.
Czyli pytanie - co zrobić aby zmienne miały poprawne wartości?
Podejrzewam, że procedure TimerProc może się odwoływać do jakiegoś innego miejsca w pamięci niż to gdzie została utorzona klasa.

Będę bardzo wdzięczny za pomoc. Pozdrawiam!

Pozostało 580 znaków

2012-11-02 20:38

Rejestracja: 7 lat temu

Ostatnio: 5 godzin temu

0

TimerProc jest metodą klasową rozumiem


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.

Pozostało 580 znaków

Taito
2012-11-02 20:53
Taito
0
unit GraphAnimation;

interface

uses Windows;

type
  TMyClass = class
    private
      Speed, Count, Frame, ID : integer;
      procedure TimerProc; 
    public
      procedure Start;
  end;

implementation

procedure TAnimation.Start;
begin
  SetTimer(0, ID, Speed, @TMyClass.TimerProc);
end;

procedure TAnimation.Animated;
begin
  Frame := Frame mod Count;
  Inc(Frame);               // tutaj jest Breakpoint
end;

end.

Okroiłem kod z tego co niepotrzebne, żeby nie zaśmiecać. Wiec sprawa dokładnie wygląda tak: po utworzeniu klasy wywołuje procedure Start. Oczywiście zmienne Count = 4 a Frame = 0 - ustawiam przy tworzeniu klasy. Tam gdzie zaznaczyłem komentarzem Breakpoint mam ustawiony Watch na obie te zmienne.
Jak program wykona się do Breakpointa zmienne maja wartości Frame = -1989922561 a Count = 1677739234...

Pozostało 580 znaków

-321oho
2012-11-02 21:08
-321oho
0

@TMyClass.TimerProc

To znaczy tyle, że wartość SELF będzie pusta/śmieciowa, bo nie ma dostępu do żadnych składowych. Kompilator nie wie o jaką klasę tobie chodzi, a ty mu rozkazałeś umieścić adres tej metody, bez żadnego kontekstu.

Rozwiązanie: Utwórz procedurę globalną która będzie wiedziała jaką metodę wywołać i którego obiektu, bo tego nie wiadomo teraz.

Pozostało 580 znaków

2012-11-02 21:22

Rejestracja: 7 lat temu

Ostatnio: 3 dni temu

Lokalizacja: Łódź

0
unit GraphAnimation;

interface

uses Windows;

type
  TMyClass = class
    private
      Speed, Count, Frame, ID : integer;
      procedure TimerProc; 
    public
      procedure Start;
  end;

Var
  MyClass: TMyClass;

implementation

procedure TAnimation.Start;
begin
  SetTimer(0, ID, Speed, @MyClass.TimerProc);
end;

procedure TAnimation.Animated;
begin
  Frame := Frame mod Count;
  Inc(Frame);                                // tutaj jest Breakpoint
end;

end.

Jeśli dobrze się orientuję to chyba jakoś tak powinno być.

Pozostało 580 znaków

Taito
2012-11-02 21:24
Taito
0

Tylko taki detal, że zależy mi żeby kazdy utworzony obiekt posiadal wlasny timer. A jesli dam procedure globalna majac z 10 obiektow a kazdy z nich inna wartosc Speed to bedzie ciezko. Chyba ze wiesz jak to ominac:)

Pozostało 580 znaków

2012-11-02 21:44
Moderator Delphi/Pascal

Rejestracja: 8 lat temu

Ostatnio: 13 godzin temu

Lokalizacja: Tuchów

0
Taito napisał(a)

Chyba ze wiesz jak to ominac:)

Wątki, wątki, wątki...


Pozostało 580 znaków

-321oho
2012-11-02 21:53
-321oho
0

SetTimer(0, ID, Speed, @MyClass.TimerProc);

To nie będzie działać. Kompilator co prawda zrozumie o co ci chodzi, ale to nie ty masz wywołać tą procedurę, tylko WinApi, a WinApi nie rozumie wywoływania klas. Kompilator nie może w wskaźniku do procedury jakieś przekazać też o jaką klasę chodzi.

Tylko taki detal, że zależy mi żeby kazdy utworzony obiekt posiadal wlasny timer. A jesli dam procedure globalna majac z 10 obiektow a kazdy z nich inna wartosc Speed to bedzie ciezko. Chyba ze wiesz jak to ominac:)

Programiści WinApi rozumią problem i dlatego definicja callbacka(której się nie trzymasz przy podawaniu swojego callbacka - grozi kreszami) wygląda tak:

MSDN napisał(a)

VOID CALLBACK TimerProc(
In HWND hwnd,
In UINT uMsg,
In UINT_PTR idEvent,
In DWORD dwTime
);

Masz tutaj też idEvent, które zawiera ID podane jako jeden z parametrów WinApi. Na tej podstawie możesz rozpoznać o który obiekt chodzi.

Wątki, wątki, wątki...

Tak, żeby je potem syncować, bawić się w masę rzeczy które nie są potrzebne. Jeżeli te procedury nie mają super dużo do roboty to nie widzę powodów aby bawić się w takie rzeczy.

Pozostało 580 znaków

Taito
2012-11-02 21:58
Taito
0

Troche nieogarniam jak wątki miałyby mi tu pomóc... Zreszta przy bardziej rozbudowanej aplikacji gdzie takich obiektow bede mial pewnie dziesiątki jak nie setki = dziesiątki/setki wątków? Samą obsługe obiektow tej klasy pewnie zrobie w wątku ale bez przesady. Chcialbym tylko uzyskac możliwość uzycia SetTimer wewnatrz klasy i zmieniac jej zmienne co pewien czas.

Pozostało 580 znaków

2012-11-02 22:39
Moderator Delphi/Pascal

Rejestracja: 8 lat temu

Ostatnio: 13 godzin temu

Lokalizacja: Tuchów

0
Taito napisał(a)

Troche nieogarniam jak wątki miałyby mi tu pomóc...

Tak jest, program piszesz w OOP, a tajmery chcesz oprzeć o funkcje WinAPI, które tak jak -123oho pisze nie rozumieją klas;

Taito napisał(a)

Zreszta przy bardziej rozbudowanej aplikacji gdzie takich obiektow bede mial pewnie dziesiątki jak nie setki = dziesiątki/setki wątków?

A tajmerów z WinAPI zrobisz też tyle samo, co obiektów? Czy ktoś każe Ci uruchamiać osobną instancję wątku dla każdego obiektu? Zamiast babrać się w przestarzałe metody skorzystaj z mniej przestarzałej klasy TThread - stwórz sobie nawet jedną instancję wątku, jeśli tyle Ci wystarczy; Poza tym masz synchronizację, która może się bardzo przydać;


Pozostało 580 znaków

-321oho
2012-11-02 22:52
-321oho
0

Zamiast babrać się w przestarzałe metody

Od kiedy to SetTimer jest przestarzałą metodą? Po prostu jest metodą dla osób które nie wymagają dużej wydajności (tak jak to jest z TThread) ani nie wymagają pierniczenia się z takimi rzeczami jak semafory, sekcje krytyczne i inne problemy w stylu deadlocków.

Poza tym masz synchronizację, która może się bardzo przydać;

A na co komu synchronizacja? Ja nigdy na to nie patrzyłem na to jako ficzur, raczej jako ograniczenie wątków które przez to nie są takie hop siup. O dziwnych błędach wynikających z niewłaściwego użycia synchronizacji nie mówiąc.

Podsumowując, zrobiłbym jeden timer który sprawdzałby wszystkie obiekty i wykonywał te które przekroczyły swój czas. Jeżeli nie wymagasz specjalnej wydajności ani idealnej dokładności to rozwiązanie jak znalazł.

Pozostało 580 znaków

Odpowiedz

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0