Nowa instancja, stary wskaźnik

0

W obsludze kliknięcia przycisku, tworzę obiekt formularza (jesli poprzednia instancja została zwolniona) i przypisuję do globalnej zmiennej (jedna zmienna dla każdej klasy formularza)

Procedure OnClick()
Begin
  If not Assigned(mojForm) then
    MojForm := TMojForm.Create();
End;

Pod różnymi przyciskami tworzę różne formularze.

W obsludze cyklicznego zdarzenia (powiedzmy OnTimer)
Wykonuję operację na aktywnym formularzu

Obsluz(Screen.ActiveForm);

Po obsłużeniu, zapamietuję formularz w celu uniknięcia kolejnej obslugi tego samego obiektu.

Procedure Obsluz(frm:TForm):
Begin
  If LIstaObsluzonych.IndexOf(frm) = -1 then
  Begin
    WlasciwaOperacja(frm);
    ListaObsluzonych.Add(obslugiwanyFormularz);
  End;
End;

Po zamknięciu formularza zwalniam jego obiekt i w destrulktorze przypisuję NIL do globalnej zmiennej.

Gdy użytkownik ponownie użyje przycisku (po zamknięciu poprzedniej instancji formularza) tworzonajest kilejna instancja.

I tu docieramy do mojego problemu:
Przy próbie Obslyzenia nowej instancji formularza, okazuje się, że nowy formularz JEST JUŻ NA LISCIE OBSLUZONYCH!

Po sprawdzeniu wychodzi na to że każda kolejna instancja formularza ma ten sam pointer!?!?!?!
Jak to możliwe? Skoro destruktor został wywolany a zmienna wyczyszczone (przypisano NIL)
Kłóci sie to z moją wiedzą na temat obiektów oraz burzy zaplanowane rozwiązanie...

Czy ktoś umie powiedzieć dlaczego nowo utworzony obiekt ma pointer taki jak obiekt przed chwilą zwolniony?
Co zrobić aby zapewnić unikalność pointerow w ramach aplikacji?
Jak inaczej zapewnić jednorazową obsługę obiektów? (Bez ingerencji w ich strukturę)

Post pisałem z komórki - problem nie daje mi spac.
Postaram się go wyedytować i uzupełnić rano gdy będę miał dostęp do kodu.

0

Wszystko fajnie, ale po co przechowujesz referencje zwolnionych formularzy? Przecież to nie ma sensu.

0
furious programming napisał(a):

Wszystko fajnie, ale po co przechowujesz referencje zwolnionych formularzy? Przecież to nie ma sensu.

Wiadomo - referencja do zwolnionego obiektu na nic się nie przyda i nie ma za tym żadnego sensu.
Jednak koszt związany z jej przechowywaniem wydaje się nieduży, a ryzyko żadne:
referencje na tej liście wykorzystywane są (w moim programie) tylko do porównywania z innymi referencjami, nigdy do dostępu do obiektów.
Obecność na liście referencji do niezwolnionego obiektu (formularza) informuje o tym, że został on już obsłużony.

Mój pomysł opierał się na tym, że każdy obiekt (tu klasy TForm) ma unikalny pointer.
Chciałem je zapamiętywać po wykonaniu operacji aby zapewnić jednorazową obsługę.

Minusem rozwiązania jest przechowywanie referencji do zwolnionych obiektów co w dłuższym czasie życia aplikacji
potencjalnie może prowadzić do przepełnienia pamięci.

Czy widzisz jakieś błędy logiczne w takim podejściu?

Jeśli masz propozycje jak usuwać referencje do zwolnionych obiektów (formularzy) z listy,
do której sam obiekt (formularz) nie ma dostępu (formularzy jest wiele i nie mam możliwości ich modyfikacji)
to chętnie o nim poczytam.

0

każdy obiekt (tu klasy TForm) ma unikalny pointer

Tutaj mam pewną wątpliwość. Nie twierdzę, że tak jest, ale widzę potencjalne ryzyko. Być może taka sytuacja nie będzie miała możliwości wystąpić, dlatego raczej pytam bardziej zorientowanych w temacie kolegów: czy jest możliwość, iż zostanie ponownie przydzielony wskaźnik o takiej samej wartości, jaka już była wykorzystana? Wiadomo, że w danej chwili nie powinno być dwóch obiektów z takim samym uchwytem, ale w chwili w której zwalniamy dany obiekt, czy wartość jego pointer/handle nie jest "zwracana do puli" dostępnych? Jeśli tak, to istnieje ryzyko, iż kolejny stworzony przez Create obiekt otrzyma "adres z recyklingu". Co sądzicie, czy taki scenariusz jest realny?

przechowywanie referencji do zwolnionych obiektów co w dłuższym czasie życia aplikacji potencjalnie może prowadzić do przepełnienia pamięci.

No bez przesady, czy operujesz milionami okienek, żeby się obawiać zapchania dostępnej pamięci? Faktem jest, że jest to lekkie marnowanie RAM, ale raczej nie widzę możliwości jego całkowitego zapchania.

0
cerrato napisał(a):

czy jest możliwość, iż zostanie ponownie przydzielony wskaźnik o takiej samej wartości, jaka już była wykorzystana?

pisząc program zakładałem że prawdopodobieństwo takiego zdarzenia jest pomijalnie małe...
i na tym oparłem pomysł na rozwiązanie zagadnienia jednorazowej obsługi.

Niestety rzeczywistość pokazała, że KAŻDA kolejna instancja formularza ma IDENTYCZNY wskaźnik... 0_o

Jest to dla mnie zaskakujące i szukam możliwego uzasadnienia dla takiej sytuacji, lub informacji że to normalne zjawisko...

3

n o ale przecież to jest najsensowniejsze rozwiązanie - tworzysz obiekt X - system rezerwuje konkretną ilość pamięci na jego dane. Nie potrzebujesz go i go zwalniasz. Po jakimś czasie znowu tworzysz obiekt X i system musi znaleźć na niego miejsce w pamięci - masz dostępny taki obszar po poprzedniej instancji obiektu X. Dostajesz wskaźnik na taki sam obszar pamięci. Z punktu widzenia działania systemu/aplikacji jest to jak najbardziej poprawne działanie. Z punktu widzenia Twojej logiki niekoniecznie. Tylko tutaj kuleje logika :p. Po prostu w destruktorze usuwaj formularz z listy i już.

0
abrakadaber napisał(a):

Po prostu w destruktorze usuwaj formularz z listy i już.

mechanizm obsługi formularzy (wraz z listą obsłużonych), jest niedostępny z poziomu formularzy.
nie ma możliwości odwołania się do listy z poziomu destruktora.

abrakadaber napisał(a):

n o ale przecież to jest najsensowniejsze rozwiązanie - tworzysz obiekt X - system rezerwuje konkretną ilość pamięci na jego dane. Nie potrzebujesz go i go zwalniasz. Po jakimś czasie znowu tworzysz obiekt X i system musi znaleźć na niego miejsce w pamięci - masz dostępny taki obszar po poprzedniej instancji obiektu X. Dostajesz wskaźnik na taki sam obszar pamięci.

Zakładając, że ponowne wykorzystanie wskaźnika dla nowej instancji obiektu to standardowe zachowanie,
czy istnieje jakiś mechanizm który mógłby to zmienić?

4
Johny_Morfina napisał(a):
abrakadaber napisał(a):

Po prostu w destruktorze usuwaj formularz z listy i już.

mechanizm obsługi formularzy (wraz z listą obsłużonych), jest niedostępny z poziomu formularzy.
nie ma możliwości odwołania się do listy z poziomu destruktora.

To wyślij komunikat, abo zaimplementuj sobie jakiś signal-slot, albo cokolwiek innego.
Albo, pewnie najlepiej, do przetrzymywania referencji formatek użyj listy przeznaczonej do obsługi takich zabaw, czyli TComponentList.
Nawet nie wiem czy to w dokumentacji jest opisane, ale jest używane w VCL i działa.

Cały ten mechanizm wykorzystuję architekturę VCL; każda niszczona kontrolka wysyła notyfikację o takiej akcji.
Takie powiadomienie dostaje również instancja TComponentList.
W przypadku kiedy obiekt, który jest na takiej liście, jest niszczony jest równie z tej listy automatycznie usuwany.
Zero problemów z lewymi referencjami...

Ale tak naprawdę, co chcesz osiągnąć? Bo jakieś dziwne to opisy i kwadratowe pomysły tu widzę...

0

o, TComponentList wydaje się ciekawe - zaraz sprawdzę.
(sprawdziłem i działa)

wloochacz napisał(a):

Ale tak naprawdę, co chcesz osiągnąć? Bo jakieś dziwne to opisy i kwadratowe pomysły tu widzę...

Mam dużą aplikację z wieloma formularzami.
muszę dodać mechanizm tłumaczący interfejs użytkownika,
który w minimalnym stopniu ingeruje w istniejący kod.

Zatem powstała procedura podmieniająca w przekazanym przez parametr formularzu co trzeba.
(korzystam z iteracji po komponentach i mechanizmu RTTI)

procedura jest wywoływana w obsłudze zdarzenia ActiveControlChanged głównego formularza i przekazywany do niej jest screen.ActiveForm.
żeby uniknąć kilkukrotnego tłumaczenia tego samego formularza, zapamiętuję przetłumaczone formularze na liście.
(jeśli screen.ActiveForm był już przetłumaczony to jest pomijany)

dzięki takiemu podejściu, nie musiałem grzebać w poszczególnych formularzach,
mechanizm obejmuje swoim działaniem wszystkie istniejące formularze, oraz te które powstaną w przyszłości.
No i mogę go łatwo zastosować w dowolnej aplikacji.

Wszelkie uwagi mile widziane.

0

Edit: trochę się spóźniłem z odpowiedzą, no ale już nie będę kasować…


Johny_Morfina napisał(a):

Wiadomo - referencja do zwolnionego obiektu na nic się nie przyda i nie ma za tym żadnego sensu.
Jednak koszt związany z jej przechowywaniem wydaje się nieduży, a ryzyko żadne:

Tzn. ryzyko istnieje, jeśli w którymś momencie przez przypadek użyjesz takiej referencji (lub zrobią to wewnętrzne mechanizmy) i dostaniesz wyjątek. Jeśli wiesz co robisz i dopuszczasz takie potencjalnie niebezpieczne rozwiązanie, to problemu nie widzę.

referencje na tej liście wykorzystywane są (w moim programie) tylko do porównywania z innymi referencjami, nigdy do dostępu do obiektów.

Takie coś można robić tylko dla istniejących instancji, bo tylko w takim przypadku porównanie adresów będzie działać prawidłowo i całkowicie przewidywalnie.

Mój pomysł opierał się na tym, że każdy obiekt (tu klasy TForm) ma unikalny pointer.
Chciałem je zapamiętywać po wykonaniu operacji aby zapewnić jednorazową obsługę.

No to stwórz sobie dwie listy – jedną z formularzami nieobsłużonymi, a drugą z obsłużonymi. W momencie, w którym potrzebujesz wykonać akcję na danym formularzu, sprawdź czy istnieje na liście tych nieobsłużonych i jesli tak – wykonaj akcję i przenieś referencję do drugiej listy (tych obsłużonych). Będziesz miał stałą złożoność pamięciową.

Takie rozwiązanie ma sens w przypadku, gdy liczba formularzy jest znana i stała oraz każda jego instancja istnieje w pamięci (formularz jest utworzony, ale niewidoczny – po prostu siedzi w pamięci i czeka na pokazanie). Ewentualnie trzymaj listę tylko z obsłużonymi oknami, których instancji po użyciu nie będzie się zwalniało z pamięci (coś à la cache okien).

Minusem rozwiązania jest przechowywanie referencji do zwolnionych obiektów co w dłuższym czasie życia aplikacji potencjalnie może prowadzić do przepełnienia pamięci.

Przepełnienie sterty raz na jakiś czas rezerwując kilka bajtów wydaje się w praktyce niemożliwe. ;)

Czy widzisz jakieś błędy logiczne w takim podejściu?

Tak, widzę. Albo tak jak napisałem wyżej, czyli zawsze trzymaj w pamięci utworzone obiekty formularzy i przerzucaj je pomiędzy listami, albo przechowuj w listach informacje o tych formularzach, które nie wymagają istnienia okien w pamięci.

Takim totalnie chałupniczym sposobem byłoby np. wykorzystanie właściwości Tag i nadanie każdemu formularzowi unikalnego numerku. Podczas wykonywania akcji, tworzy się formularz w pamięci i sprawdza, czy jego Tag istnieje na liście – jeśli nie to się kontynuuje i dodaje ten numerek do listy, a jeśli tak to się zwalnia formularz i wyświetla informację. Jeśli każde okno ma inny tytuł, to zamiast właściwości Tag można skorzystać z Caption. Do tego wystarczy jedna lista, z danymi o obsłużonych formularzach.

Wszystko zależy jednak od tego, co to za aplikacja, ile masz tych okien, czy ich liczba jest znana czy może są one generowane w locie, w jakim środowisku ona pracuje (jaka platforma i ile pamięci, skoro obawiasz się przepełnienia) itd.

0

przepełnienia pamięci się nie obawiam - wspomniałem o nim bo to jedyne ryzyko jakie widzę (pomijalnie małe)

formularze są tworzone i zwalniane dynamicznie nie znam nawet liczby klas formularzy (jest ich "milion", często dochodzą nowe czasem stare są kasowane)

dwie listy nie są konieczne. formularz albo jest obsłużony albo nie jest - do tego wystarczy jedna lista (obsłużonych) - jeśli go nie ma na tej liście to znaczy ze nie został obsłużony (problem był co robić ze zwolnionymi formularzami)

porównanie referencji będzie działało poprawnie nawet w przypadku zwolnionych obiektów. Przestanie działać kiedy nowo tworzony obiekt dostanie to samo miejsce w pamięci co zwolniony wcześniej obiekt. Niestety na takie zjawisko właśnie się natknąłem chociaż się go nie spodziewałem:/

wykorzystanie pola Tag będzie ostatnią deska ratunku. to średni pomysł bo często wykorzystywany i nigdy nie mam pewności czy ktoś już z niego nie skorzystał przy innej okazji.

wykorzystanie pola Caption nie wchodzi w grę, mój mechanizm zmienia to pole "...i cały misterny plan w pizdu"

Ostatnie dwa punkty są dodatkowo nieakceptowalne ponieważ uzależniają działanie mechanizmu od pól, które mogą być modyfikowane bez kontroli.
Wybieram rozwiązanie zasugerowane przez @wloochacz opierające się na TComponentList

0

nigdy nie mam pewności czy ktoś już z niego nie skorzystał przy innej okazji

W jaki sposób ktoś może grzebać we właściwościach komponentów/formularzy w TWOJEJ aplikacji? Jakoś tego nie łapię...

0
cerrato napisał(a):

nigdy nie mam pewności czy ktoś już z niego nie skorzystał przy innej okazji

W jaki sposób ktoś może grzebać we właściwościach komponentów/formularzy w TWOJEJ aplikacji? Jakoś tego nie łapię...

skąd założenie, że to moja aplikacja :D
jestem jednym z wielu programistów i to świeżym w zespole.
dlatego zależy mi na rozwiązaniu nie ingerującym w istniejący kod.
(zawsze dobrze jest pisać kod nie zależny od innych modułów i tego co się w nich znajduje)

1

jestem jednym z wielu programistów i to świeżym w zespole.

No ale nie macie jakichś standardów pracy ustalonych, jakichś wytycznych, nie możesz się dowiedzieć czy ktoś inny z zespołu nie korzysta z tag? Trochę dziwne jest to, o czym piszesz. No bo albo pracujesz jako solista - wtedy rób co tylko Ci się podoba, albo macie jakąś drużynę - w takim układzie powinna jakoś komunikacja między jej członkami istnieć, bo inaczej zapanuje totalny chaos.

1
Johny_Morfina napisał(a):

o, TComponentList wydaje się ciekawe - zaraz sprawdzę.
(sprawdziłem i działa)

wloochacz napisał(a):

Ale tak naprawdę, co chcesz osiągnąć? Bo jakieś dziwne to opisy i kwadratowe pomysły tu widzę...

Mam dużą aplikację z wieloma formularzami.
muszę dodać mechanizm tłumaczący interfejs użytkownika,
który w minimalnym stopniu ingeruje w istniejący kod.

OK, ale pytanie - czy jest jakaś forma bazowa, z której dziedziczą wszystkie inne w tym projekcie?

Zatem powstała procedura podmieniająca w przekazanym przez parametr formularzu co trzeba.
(korzystam z iteracji po komponentach i mechanizmu RTTI)

To chyba najgorsze możliwe rozwiązanie, bo jedno z najwolniej działających.
No, ale działać działa...

procedura jest wywoływana w obsłudze zdarzenia ActiveControlChanged głównego formularza i przekazywany do niej jest screen.ActiveForm.

Coś chyba nie tak; TForm nie ma takiego zdarzenia.
Takie darzenia ma Screen.OnActiveFormChange oraz Screen.OnActiveControlChange.

żeby uniknąć kilkukrotnego tłumaczenia tego samego formularza, zapamiętuję przetłumaczone formularze na liście.
(jeśli screen.ActiveForm był już przetłumaczony to jest pomijany)

No to w sumie, wystarczy to co pokazałem.
Aczkolwiek ten temat można zrealizować zupełnie inaczej, może nawet zdecydowanie lepiej.
Ale... do tego trzeba wiedzieć zdecydowanie więcej o tym, jak ta aplikacja w ogóle jest napisana - patrz z pierwsze pytanie.

dzięki takiemu podejściu, nie musiałem grzebać w poszczególnych formularzach,
mechanizm obejmuje swoim działaniem wszystkie istniejące formularze, oraz te które powstaną w przyszłości.
No i mogę go łatwo zastosować w dowolnej aplikacji.

Lepiej nie rób tego, są lepsze metody na osiągnięcie tego celu.

Wszelkie uwagi mile widziane.

Uwagi, aby były konstruktywne, muszą opierać się na jakimś kontekście.
A więc chcesz dostać konstruktywną odpowiedź, opisz jak najwięcej.

0
wloochacz napisał(a):

OK, ale pytanie - czy jest jakaś forma bazowa, z której dziedziczą wszystkie inne w tym projekcie?

Tak jest... TForm :D

wloochacz napisał(a):

No, ale działać działa...

I o to chodzi... i o to chodzi!

wloochacz napisał(a):

Takie darzenia ma Screen.OnActiveFormChange oraz Screen.OnActiveControlChange.

Tak, masz rację. zdarzenie należy do Screen ale procedura obsługi jest napisana w głównym formularzu (nie pytaj dlaczego tak bo nie wiem)

wloochacz napisał(a):

No to w sumie, wystarczy to co pokazałem.

no i własnie tak zrobiłem - dzięki za podpowiedź.

wloochacz napisał(a):

Aczkolwiek ten temat można zrealizować zupełnie inaczej, może nawet zdecydowanie lepiej.
Ale... do tego trzeba wiedzieć zdecydowanie więcej o tym, jak ta aplikacja w ogóle jest napisana - patrz z pierwsze pytanie.

Mówisz o zapewnieniu jednorazowego wywołania procedury czy o całym tłumaczeniu?

Wiem, że rozwiązanie nie jest idealne ale spośród kilku zaproponowanych sposobów
i wobec braku wspólnej klasy "Góra" zdecydowała się na takie rozwiązanie.

1
Johny_Morfina napisał(a):
wloochacz napisał(a):

OK, ale pytanie - czy jest jakaś forma bazowa, z której dziedziczą wszystkie inne w tym projekcie?

Tak jest... TForm :D

OK, nie mam więcej pytań...
A na tej podstawie można pewne wnioski wysnuć, które nie będą pozytywne dla tego projektu...

wloochacz napisał(a):

No, ale działać działa...

I o to chodzi... i o to chodzi!

Nie, wcale nie o to chodzi.
Ponieważ potem, takie potworki rodzą następne potworki, które zabezpieczają doraźne potrzeby...

wloochacz napisał(a):

Takie darzenia ma Screen.OnActiveFormChange oraz Screen.OnActiveControlChange.

Tak, masz rację. zdarzenie należy do Screen ale procedura obsługi jest napisana w głównym formularzu (nie pytaj dlaczego tak bo nie wiem)

wloochacz napisał(a):

No to w sumie, wystarczy to co pokazałem.

no i własnie tak zrobiłem - dzięki za podpowiedź.

wloochacz napisał(a):

Aczkolwiek ten temat można zrealizować zupełnie inaczej, może nawet zdecydowanie lepiej.
Ale... do tego trzeba wiedzieć zdecydowanie więcej o tym, jak ta aplikacja w ogóle jest napisana - patrz z pierwsze pytanie.

Mówisz o zapewnieniu jednorazowego wywołania procedury czy o całym tłumaczeniu?

O jednym i drugim, ze wskazaniem na tłumaczenie.

Wiem, że rozwiązanie nie jest idealne ale spośród kilku zaproponowanych sposobów
i wobec braku wspólnej klasy "Góra" zdecydowała się na takie rozwiązanie.

Tak, tylko dodanie klasy potomnej to raczej "pikuś".
A daje to dość pokaźne możliwości do rozwoju w późniejszym czasie.
Ja bym się długo nie zastanawiał i po prostu bym to zrobił.

0
wloochacz napisał(a):

A na tej podstawie można pewne wnioski wysnuć, które nie będą pozytywne dla tego projektu...

wloochacz napisał(a):

Ponieważ potem, takie potworki rodzą następne potworki, które zabezpieczają doraźne potrzeby...

projekt ma się świetnie rozwijając się w ten sposób od kilkunastu lat.
Ostatnio (przed moim przyjściem) podobno był krytyczny moment...
Firma po rozważeniu za i przeciw postanowiła go kontynuować...

Potworek żyje i ma się dobrze!

3

Nie można zakładać ani że nowy wskaźnik będzie taki sam jak stary, ani że zawsze będzie inny. Bo to może się zmienić zupełnie niespodziewanie. Trzymanie referencji do nieistniejących już obiektów jest bezcelowe.

1
Johny_Morfina napisał(a):

przepełnienia pamięci się nie obawiam - wspomniałem o nim bo to jedyne ryzyko jakie widzę (pomijalnie małe)

Panowie, przecież każdy z was wie ile pamięci zajmie zmienna-referencja na obiekt, prawda? ;-)
To o czym tu mowa...

formularze są tworzone i zwalniane dynamicznie nie znam nawet liczby klas formularzy (jest ich "milion", często dochodzą nowe czasem stare są kasowane)

Formularze są tworzone dynamicznie czy obiekty klasy formularza są tworzone dynamicznie?
Pytam z ciekawości, bo jeszcze nie spotkałem nikogo (oprócz siebie, oczywiście :P) kto tworzy całkowicie dynamicznie całe formularze :D

dwie listy nie są konieczne. formularz albo jest obsłużony albo nie jest - do tego wystarczy jedna lista (obsłużonych) - jeśli go nie ma na tej liście to znaczy ze nie został obsłużony (problem był co robić ze zwolnionymi formularzami)

porównanie referencji będzie działało poprawnie nawet w przypadku zwolnionych obiektów. Przestanie działać kiedy nowo tworzony obiekt dostanie to samo miejsce w pamięci co zwolniony wcześniej obiekt. Niestety na takie zjawisko właśnie się natknąłem chociaż się go nie spodziewałem:/

Z tego co wiem, to nie, nie będzie działało poprawnie.
Nie da się sprawdzić ważności referencji.
Można sprawdzić czy zmiana przechowuje nil, ale jeśli zrobisz tak:

var
  lForm1 : TForm;
  lForm2 : TForm;
begin
   lForm1 := TForm.Create(nil);
   lForm2 := lForm1;

   lForm1.Free;
end;

To w zmiennej lForm2 będziesz miał śmiecia, a nie referencję. czy nil.
Oczywiście możesz rzutować to na NativeInt i porównywać wartość numerków, ale co do zasady to nie jest to samo co porównanie referencji.
A więc cały twój wcześniejszy pomysł jest do kosza - przykro mi, ale taka prawda.

wykorzystanie pola Tag będzie ostatnią deska ratunku. to średni pomysł bo często wykorzystywany i nigdy nie mam pewności czy ktoś już z niego nie skorzystał przy innej okazji.

Dlatego też nie powinno się go dotykać, zwłaszcza we własnych klasach.
Dlatego pytałem, czy te formularze mają wspólnego przodka w tym projekcie.
To trochę dziwne, ze nie mają.
U mnie nawet datasety mają takiego wspólnego przodka..

wykorzystanie pola Caption nie wchodzi w grę, mój mechanizm zmienia to pole "...i cały misterny plan w pizdu"

Boziu, ciekawe co jeszcze tu przeczytam w obszarze nowych pomysłów ;-)

Ostatnie dwa punkty są dodatkowo nieakceptowalne ponieważ uzależniają działanie mechanizmu od pól, które mogą być modyfikowane bez kontroli.
Wybieram rozwiązanie zasugerowane przez @wloochacz opierające się na TComponentList

I dla Ciebie to najprostsze rozwiązanie, a poza tym raczej na pewno będzie działać poprawnie.
No chyba, że są tam formularze w DLL :P

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