Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Круглый стол
  
Правила КС
>> Настройки

Фильтр вопросов
>> Новые вопросы
отслеживать по
>> Новые ответы

Избранное

Страница вопросов
Поиск по КС


Специальные проекты:
>> К л ю к в а
>> Г о л о в о л о м к и

Вопрос №

Задать вопрос
Off-topic вопросы

Помощь

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  22:35[Войти] | [Зарегистрироваться]
Ответ на вопрос № 75600

11-02-2010 01:08
Добрый день, славные жители Королевства.

1. Имеется родительское окно, оно открывается при запуске программы. Назовем его WinROD.
2. Далее из меню WinROD открываются еще два окошка Win_1 и Win_2.
3. В Win_1 кнопка, при нажатии котрой в Win_2 должен обновиться SQL запрос (F_Win_2.Query.Refresh).
4. Пробовал конструкции:
  4.1. if Assigned(F_Win_2) то рефреш
  4.2. если F_Win_2.Visible то рефреш

Обе они работают, если Win_2 открыто, в противном случае - ПК выдает ошибку.

В чем косяк – не понятно… Может кто подскажет?

В принципе нужно организовать проверку на существование Win_2, вот и все. Но как это сделать??

[+] Добавить в избранные вопросы

Отслеживать ответы на этот вопрос по RSS

Ответы:


Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице.
Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.

18-05-2010 10:25 | Комментарий к предыдущим ответам
Поскольку собираюсь дать сюда ссылку в другом вопросе, считаю необходимым обратить особое внимание на то, что на момент уничтожения контролируемой формы необходимо обеспечить существование переменной, на которую сохраняется ссылка. В противном случае будет портиться случайная память.
Поэтому:
1. Переменная не должна быть полем другой MDIChild-формы (контролирующей), т.к. эта форма может быть уничтожена раньше контролируемой. Либо при своём уничтожении контролирующая форма сначала должна уничтожать контролируемую.
2. Переменная не должна быть элементом динамического массива, т.к. при SetLength динамические массивы перераспределяются.

Т.е. это может быть либо глобальная переменная, либо элемент глобального статического массива, либо поле главной формы (отдельная переменная или элемент статического массива).

23-04-2010 12:49
Попробую подробнее прокомментировать свой код.

type
  TChild1Form = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FPVarForNil: PPointer;
  public
    constructor Create(var AVarForNil: TChild1Form; AOwner: TComponent); reintroduce;
    destructor Destroy; override;
  end;


Если в своём классе в конструкторе не требуется выполнять дополнительных действий, то его и не объявляют. Тогда при создании класса будет вызываться наследуемый конструктор. Для формы это будет TCustomForm.Create:

constructor Create(AOwner: TComponent); override;


Если нужен свой конструктор, то обычно перекрывают наследуемый виртуальный, в своём выполняют нужные действия и вызывают наследуемый, т.е. если бы нам не нужно было передавать в свой конструктор дополнительных параметров, то логичнее всего его было объявить так-же, как в TCustomForm.
Поскольку нам нужно передать дополнительный параметр, то мы объявляем свой конструктор с другим набором параметров.
Теперь имеем ситуацию, когда наш конструктор имеет то-же имя, что и наследуемый, но другой набор параметров. Поэтому он не может перекрывать наследуемый виртуальный конструктор, т.е. мы не можем написать ему "override". Но поскольку имя совпадает, то наш конструктор скрывает наследуемый, т.е. его нельзя будет вызвать снаружи (только из наследников, указывая ключевое слово inherited).
Компилятор отслеживает такие ситуации и считает их подозрительными, поэтому выдаёт по этому поводу предупреждение. Чтобы избавиться от предупреждения, мы объявляем свой конструктор с ключевым словом reinroduce, давая компилятору понять, что данная ситуация - не следствие ошибки, а именно так всё и было задумано, поэтому ему не следует беспокоиться по этому поводу и выдавать нам предупреждение.
В принципе, свой конструктор может иметь любое имя, и мы могли бы объявить его, например, так:

    constructor SuperCreate(var AVarForNil: TChild1Form; AOwner: TComponent);


Однако в этом случае оставалась бы возможность создать нашу форму через вызов наследуемого конструктора, который не учитывает нашу специфику.
В прародителе всех классов TObject конструктор, деструктор и метод Free объявлены так:

  TObject = class
    constructor Create;
    procedure Free;
    destructor Destroy; virtual;
  end;

Метод Free очень прост:

procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;


Что такое Self...
Метод вообще, а в данном конкретном случае, Free, можно рассматривать как отдельную процедуру:

procedure Free(Self: TObject);
begin
  if Self <> nil then
    Destroy;
end;

При этом вызов Obj.Free можно рассматривать как Free(Obj)
Никакого другого волшебного смысла переменная Self не несёт. Внутри метода никакими другими способами, иначе как через переменную Self, нельзя узнать "свой объект". Вся работа со своим объектом внутри метода идёт только через Self. Другое дело, что в большинстве случаев его явно не указывают, если это не приводит к неоднозначности, но он всегда подразумевется. Это так называемый "неявный Self". Т.е. процедура Free могла быть записана так:

procedure TObject.Free;
begin
  if Self <> nil then
    Self.Destroy;
end;


При вызове Obj.Free внутрь Free, а затем и Destroy передаётся копия указателя Obj, т.е. внутри каждого метода существует своя переменная Self, которой присваивается значение, хранящееся в переменной Obj. По этому внутри метода мы можем что угодно делать с переменной Self (хотя это - изврат), но не можем изменить ту переменную, значение которой было присвоено Self при вызове метода, а также не имеем никакой информации о том, где она находтся.
Пара слов о передаче параметров в процедуры... Параметры могут передаваться двумя способами:
1. По значению. При этом внутри процедуры параметр можно рассматривать как отдельную переменную, изменение которой никак не влияет на внешнюю.
2. По ссылке. В этом случае переменная внутри процедуры - это та-же самая переменная, что и снаружи, и изменение параметра внутри процедуры, соответственно, приводит к изменению внешней переменной. Такой способ накладывает некоторые дополнительные ограничения, в частноси, по ссылке нельзя передавать свойства. Ссылочная природа такого параметра реализуется за кадром и работа с ним ничем не отличается от работы с параметром, переданным по значению.

procedure SomeProc(A: Integer);  // По значению
procedure SomeProc(var A: Integer);  // По ссылке


Для того, чтобы иметь возможность добраться до переменной, в которой хранится ссылка на нашу форму, мы в явном виде передаём её конструктору как параметр, передаваемый по ссылке. При этом "ссылочность" нужна не для изменения исходной переменной, а для того, чтобы получить её адрес через оператор @. Для хранения указателя, ссылающегося на переменную, в которой хранится ссылка на форму, мы заводим приватное поле FPVarForNil: PPointer;
Тип PPointer объявлен в модуле System: PPointer = ^Pointer;
Т.е. это указатель на указатель. Нетипизированный указатель совместим по присваиванию и сравнению с указателями любого типа без необходимости его приведения к какому-либо типу.
При уничтожении формы в деструкторе мы выполняем строчку:
  FPVarForNil^ := nil;
Т.е. переменной, на которую ссылается переменная FPVarForNil, присваиваем значение nil. Т.е. обнуляем ту самую переменную, которую мы указывали конструктору в качестве первого параметра.

В конструкторе и деструкторе вызываем соответственно наследуемые конструктор и деструктор.
Когда объявление текущего метода совпадает с объявлением наследуемого, можно указывать только слово inherited
Если есть параметры, то они передаются соответственно в наследуемый метод. В нашем случае объявления конструкторов не совпадают, поэтому имя и параметр указываем явно, а деструктор - совпадает, поэтому ограничивается только inherited (хотя, можно было бы написать и inherited Destroy).

Про Action := caFree; уже писал. Упомяну только, что нельзя уничтожать объект внутри его собственных обработчиков. При возникновении такой необходимости применяют метод Release. Его вызов приводит не к немедленному уничтодению объекта, а к отложенному - после выхода из всех обработчиков. Присвоение значения caFree параметру Action, переданному по ссылке, является косвенным способом вызова метода Release.
Загляните ещё в »вопрос КС №72638«

Ещё по поводу деструктора...
Поскольку деструктор является виртуальным, начиная с TObject, то для вызова Free можно использовать переменную типа любого предка объекта - деструктор всё равно будет вызван правильный - реального типа объекта, а не типа переменной.

По поводу TMainForm.SpeedButton1Click...
Формы с FormStyle=fsMDIChild отображаются сразу после создания, поэтому для них не следует вызывать Show.

Автосоздаваемые формы создаются в файле проекта (.dpr) строчками вроде такой:
Application.CreateForm(TMainForm, MainForm);
Такой способ создания формы не может обеспечить вызов деструктора, объявление которого не совпадает с наследуемым.
В своём коде создавать формы следует только путём вызова конструктора и никогда не применять Application.CreateForm.

P.S. На самом деле, если предполагается создавать одновременно не более одной такой формы, то можно было ограничиться обнулением одной совершенно конкретной глобальной переменной в деструкторе и даже не писать своего конструктора:

unit Child1_Form;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TChild1Form = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  public
    destructor Destroy; override;
  end;

var
  Child1Form: TChild1Form;

implementation

{$R *.dfm}

destructor TChild1Form.Destroy;
begin
  inherited;
  Child1Form := nil;
end;

procedure TChild1Form.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree; 
end;

end.


Однако я предпочёл привести более универсальный вариант.

P.P.S. По поводу глобальных переменных... Посмотрите »вопрос КС №75155« Там в первом ответе в пункте 5 описана очень коварная ошибка.

23-04-2010 01:42
>>> Подскажи литературу, в которой подробно рассказано про "конструкторы" и "деструкторы".

К сожалению, подробного рассказа о конструкторах и деструкторах в одном месте я не встречал. Мельком, наверное, упоминаются во многих книгах по Delphi.
Из тех обсуждений, которые были здесь, думаю, полезно будет посмотреть следующие:
»вопрос КС №41889«
»вопрос КС №69171«
»вопрос КС №69494«
»вопрос КС №72174«
»вопрос КС №73055«
»вопрос КС №73797«
»вопрос КС №75663«

Можно просмотреть также другие вопросы, отнесённые модератором к разделу "Конструктор/деструктор класса":
http://www.delphikingdom.com/asp/itemq.asp?ItemID=369

В любом случае следует хорошо уяснить, что такое виртуальные методы.
Посмотрите »вопрос КС №60791«
Там Ins кратенько объясняет их суть на пальцах.

Посмотрите раздел "Лицей". Там были очень неплохие серии статей, возможно что-то писалось и о конструкторах/деструкторах.

Свой код прокомментирую позже.

20-04-2010 19:22 | Сообщение от автора вопроса
Бел_Амор, спасибо за код. Проверил - работает. Однако (к большому сожалению) у меня не хватает знаний, чтобы его полностью осмыслить.

Подскажи литературу, в которой подробно рассказано про "конструкторы" и "деструкторы".

Всего тебе доброго.

16-04-2010 01:11
>>> 1. Имеется родительское окно, оно открывается при запуске программы. Назовем его WinROD.

Я бы назвал его MainForm.

>>> Каким образом явно обнулить уаказатель?

Например, сохранить в поле самой формы указатель на переменную, которую следует обнулять при своём уничтожении и в деструкторе обнулять её. Указывать эту переменную логично в конструкторе.

Форму я уничтожаю именно через Free
// --------- закрыть окно при нажатии на крестик 
procedure TF_Vodila.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    Action:=caFree;
end;


Вообще-то, не через Free, а через Release.
Строчка Action:=caFree ничего не уничтожает. Она всего лишь присваивает значение локальной переменной в TCustomForm.Close, которое проверяется после выхода из обработчика и в случае необходимости вызывается Release.

unit Main_Form;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
  TMainForm = class(TForm)
    SpeedButton1: TSpeedButton;
    procedure SpeedButton1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm; // FormStyle=fsMDIForm

implementation

uses Child1_Form;

{$R *.dfm}

procedure TMainForm.SpeedButton1Click(Sender: TObject);
begin
  if Assigned(Child1Form) then
    ShowMessage('Форма уже существует')
  else
    Child1Form := TChild1Form.Create(Child1Form, Application);
end;

end.


unit Child1_Form;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TChild1Form = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FPVarForNil: PPointer;
  public
    constructor Create(var AVarForNil: TChild1Form; AOwner: TComponent); reintroduce;
    destructor Destroy; override;
  end;

var
  Child1Form: TChild1Form; // FormStyle=fsMDIChild, Неавтосоздаваемая!!!

implementation

{$R *.dfm}

{ TChild1Form }

constructor TChild1Form.Create(var AVarForNil: TChild1Form; AOwner: TComponent);
begin
  inherited Create(AOwner);
  FPVarForNil := @AVarForNil;  // Параметр объявлен с var
end;

destructor TChild1Form.Destroy;
begin
  FPVarForNil^ := nil;
  inherited;
end;

procedure TChild1Form.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree; 
end;

end.

В этом примере я использовал глобальную переменную, но это, естественно, не обязательно. Переменная может быть, например, полем (но не свойством) класса. Только нужно быть очень осторожным, чтобы не получилось так, что в форме останется ссылка на поле уже уничтоженного экземпляра какого-либо класса.

15-04-2010 20:59 | Сообщение от автора вопроса
Напоминаю ваш ответ:

">>> В принципе нужно организовать проверку на существование Win_2, вот и все. Но как это сделать??
И что же вы делаете?

// Проверить, что форма создана:
if Assigned(F_WindowD2) then



Вы здесь проверяете не то, что форма создана, а то, что указатель, который вы собираетесь использовать для доступа к форме, не равен nil... А он не равен nil сразу же после присвоения ему ссылки, которую вернул конструктор после создания формы, и до тех пор, пока вы его явно не обнулите. Если вы уничтожили форму через Free, то он сам не обнулится... "

Внимание вопрос :))) :
Каким образом явно обнулить уаказатель?

Форму я уничтожаю именно через Free

// --------- закрыть окно при нажатии на крестик 
procedure TF_Vodila.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    Action:=caFree;
end;


12-02-2010 00:04
Сделал так:
...
Помогло :)


Это - не решение... Это - верёвочки и завязочки с бантиками...
Годится только для того, чтобы один раз показать, что, мол, работает...
Причины проблемы не выяснены. Соответственно, решение не основывается на правильном понимании ситуации.
Вы совершенно правильно поставили вопрос:
>>> В принципе нужно организовать проверку на существование Win_2, вот и все. Но как это сделать??
И что же вы делаете?

// Проверить, что форма создана:
if Assigned(F_WindowD2) then


Вы здесь проверяете не то, что форма создана, а то, что указатель, который вы собираетесь использовать для доступа к форме, не равен nil... А он не равен nil сразу же после присвоения ему ссылки, которую вернул конструктор после создания формы, и до тех пор, пока вы его явно не обнулите. Если вы уничтожили форму через Free, то он сам не обнулится... Обязательно прочитайте »вопрос КС №57952«

// Проверить, что форма открыта и показывается:
if F_WindowD2.Showing then
F_WindowD2.ZROQuery_VOnWork.Refresh;


Вот она, завязочка с бантиками... Поскольку ваша проверка через Assigned срабатывает только на то, что форма ещё ни разу не была создана, а после создания и уничтожения уже выдаёт ложную информацию, то всю нагрузку на себя берёт проверка Showing уже, возможно, уничтоженного объекта... То, что эта проверка пока выдаёт False для уничтоженной формы, а не True или не вызывает исключения - случайность, на которую ни в коем случае нельзя полагаться.
Запросу совершенно безразлично, показывается в данный момент форма, или нет. Главное - чтобы она существовала. Так что решение здесь такое: при уничтожении формы обнулять переменную, а потом просто проверять её на равенство nil хоть через <>nil, хоть через Assigned...
Это - решение крупным планом. Могут быть тонкости при обнулении переменной.

11-02-2010 17:56 | Сообщение от автора вопроса
Спасибо, ребята. Сделал так:


// Проверить, что форма создана:
if Assigned(F_WindowD2) then
// Проверить, что форма открыта и показывается:
if F_WindowD2.Showing then
F_WindowD2.ZROQuery_VOnWork.Refresh;



Помогло :)

11-02-2010 14:27
4.2. если F_Win_2.Visible - ето может тоже гдето вилести.ІМХО Проверяй лутше так

// Проверить, что форма создана:
if Assigned(F_Win_2) then
// Проверить, что форма открыта и показывается:
if F_Win_2.Showing then

11-02-2010 04:58
Уберите все Query и Table с форм и поместите их в DataModule, и тогда многие проблемы отпадут сами собой.

11-02-2010 02:13
В принципе нужно организовать проверку на существование Win_2, вот и все. Но как это сделать??
А как создаётся окно? Какая ошибка?
Если Win_2 не создано, а переменная, содержащая указатель на него является
атрибутом класса (например, формы TWinROD), проверьте: F_Win_2 <> nil.
Если нет, то обнуляйте переменную во время загрузки программы и после
уничтожения окна.
Можете попробовать функцию IsWindow(Win_2.Handle).

Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
Кто съел Красную шапочку?
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.
Тип сообщения:
Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве

    Страница избранных вопросов Круглого стола.
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования