Элемент управления «ххх» не имеет родительского окна - PullRequest
9 голосов
/ 23 сентября 2010

Я пытался написать библиотеку dll в Delphi с функцией, которая создает экземпляр потомка TFrame и возвращает его.Но когда я импортировал эту функцию в приложение, каждый раз, когда я вызывал ее, я получал исключение типа «элемент управления« xxx »не имеет родительского окна».Я не уверен на 100%, но в конструкторе этого класса появилось исключение при обращении к любому из элементов управления GUI.

Не могли бы вы сказать мне, в чем причина такого поведения?Должен ли я просто использовать потомков TForm или есть лучшее решение?

Спасибо!

Ответы [ 6 ]

8 голосов
/ 23 сентября 2010

Об ошибке

Это сообщение об ошибке выдается из блока Controls.pas из метода TWinControl.CreateWnd. По сути, этот код используется для создания дескриптора Window для вашего потомка TWinControl (TFrame, TButton, TEdit ... если он может иметь фокус клавиатуры, то это потомок TWinControl), и на самом деле это очень разумное сообщение об ошибке: вы не можете иметь Window без WindowParent, и так как мы говорим о VCL здесь, имеет смысл попытаться получить дескриптор родительского окна из TWinControl.Parent; И это не назначено.

Это НЕ ПОЧЕМУ появляется сообщение об ошибке. Вы видите это сообщение об ошибке, потому что для некоторого кода, который вы используете для настройки фрейма, для какой-либо операции требуется дескриптор Window. Это может быть что угодно, например, установка Заголовка некоторого компонента (который внутренне требует, чтобы дескриптор окна выполнял некоторые вычисления). Лично я действительно ненавижу, когда это происходит. Когда я создаю GUI из кода, я стараюсь максимально задержать назначение Parent, чтобы задержать создание окна, поэтому меня это укусило много раз.

В зависимости от использования DLL, возможное исправление

Я надену шляпу для чтения психов. Так как вам нужно вернуть FRAME из вашей DLL, и вы не можете вернуть фактический Frame, потому что это специфичный для Delphi объект, и вам не разрешено возвращать специфичные для Delphi объекты через границы DLL, я предполагаю, что вы возвращаете дескриптор окна, как и все хорошие API, использует определение функции, например:

function GiveMeTheNiceFrame:HWND;

Проблема в том, что подпрограмма требует создания фактического дескриптора окна путем вызова TWinControl.CreateWnd, и, в свою очередь, этот вызов требует дескриптора родительского окна для установки вызова на Windows.CreateWindowEx, и подпрограмма может не получить дескриптор родительского окна, поэтому он выдает ошибки.

Попробуйте заменить вашу функцию на что-то вроде:

function GiveMeTheNiceFrame(OwnerWindow:HWND):HWND;
begin
  Result := TMyNiceFrame.CreateParanted(OwnerWindow).Handle;
end;

... т.е.: используйте конструктор CreateParented(AParentWindow:HWND), а не обычный Create(AOwner:TComponent) и передайте HWND владельца в свою DLL.

1 голос
/ 23 сентября 2010

Есть несколько важных вещей, которые следует запомнить:

  1. При использовании DLL у вашей DLL и EXE есть экземпляр Application, который борется за контроль.Элементы управления в вашей DLL будут видеть экземпляр приложения, который принадлежит DLL;Элементы управления в вашем EXE увидят экземпляр приложения, который принадлежит EXE.Этой борьбы не возникает при использовании пакетов, поскольку тогда будет только один экземпляр приложения.
  2. Фреймы - это элементы управления, но они не являются формами.
  3. При использовании элементов управления в приложении они невизуально существуют без родительского элемента управления (обычно это форма или контейнер, который имеет родительскую иерархию по отношению к форме).
  4. Некоторые элементы управления не могут предоставить полную функциональность, если они не существуют визуально и не имеют действительного родителя.

Попробуйте воспроизвести вашу проблему внутри EXE-файла;если вы не можете воспроизвести, возможно, это первое, что есть в приведенном выше списке.

- jeroen

0 голосов
/ 30 декабря 2014

Я думаю, что это очень крутое решение.Я думаю, что раньше это не пробовали :) Я использую фиктивного родителя (который является формой).

function MyFrame_Create(hApplication, hwndParent:THandle; X, Y, W, H:Integer):Pointer; stdcall;
var Fr: TMyFrame;
    F:  TForm;
    CurAppHandle: THandle;
begin
  CurAppHandle:=Application.Handle;
  Application.Handle:=hApplication;
  //---
  F:=TForm. Create(Application);//Create a dummy form
  F.Position:=poDesigned;
  F.Width:=0; F.Top:=0; F.Left:=-400; F.Top:=-400;//Hide Form
  F.Visible:=True;
  //---
  Fr:=TMyFrame.Create(Application);
  Fr.Parent:=F;//Set Frame's parent
  //Fr.ParentWindow:=hwndParent;
  Windows.SetParent(Fr.Handle, hwndParent);//Set Frame's parent window
  if CurAppHandle>0 then Application.Handle:=CurAppHandle;
  //---
  Fr.Left:=X;
  Fr.Top:=Y;
  Fr.Width:=W;
  Fr.Height:=H;
  Result:=Fr;
end;//MyFrame_Create

procedure MyFrame_Destroy(_Fr:Pointer); stdcall;
var Fr: TMyFrame;
    F: TObject;
begin
 Fr:=_Fr;
 F:=Fr.Parent;
 Fr.Parent:=Nil;
 if (F is TForm) then F.Free;
 //SetParent(Fr.Handle, 0);
 //Fr.ParentWindow:=0;
 Fr.Free;
end;//MyFrame_Destroy
0 голосов
/ 18 ноября 2011

Вы можете избежать этого сообщения, назначив nil родительскому событию OnClose, иногда оно работает:

SomeControl.Parent := nil;//Before free your TControl
SomeControl.Free;
0 голосов
/ 18 марта 2011

Я нашел это (CreateParams вызывается как часть CreateWnd):

procedure TCustomFrame.CreateParams(var Params: TCreateParams);
begin
  inherited;
  if Parent = nil then
    Params.WndParent := Application.Handle;
end;

И Application.Handle = 0, поэтому он всегда выдает ошибку позже в CreateWnd.
После прочтения Delphi: Как вызвать унаследованного наследуемого предка для виртуального метода?

Я решил это, переопределив CreateParams в моем кадре, чтобы пропустить версию tCustomFrame:

type
  tCreateParamsMethod = procedure(var Params: TCreateParams) of object;

type
  tMyScrollingWinControl = class(TScrollingWinControl);

procedure TDelphiFrame.CreateParams(var Params: TCreateParams);
var
  Proc: tCreateParamsMethod;
begin
  TMethod(Proc).Code := @TMyScrollingWinControl.CreateParams;
  TMethod(Proc).Data := Self;

  Proc(Params);
end;

Теперь он просто выдает ошибки при попытке установить фокус на субконтролях, что, я думаю, я исправлю, перехватив WM_FOCUS, но мы рассмотрим, как это будет происходить.

function CreateFrame(hwndParent: HWnd): HWnd; stdcall;
var
  frame: tFrame;
begin
  Result := 0;
  try
    frame := TDelphiFrame.CreateParented(hwndParent);
    Result := frame.Handle;
  except on e: Exception do
    ShowMessage(e.Message);
  end;
end;
0 голосов
/ 23 сентября 2010

Похоже, вам просто нужно назначить компонент (форму или часть формы, например панель), который удерживает рамку, в theframe.parent.

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

Переместите код GUI в onshow или процедуру, которую вы вызываете явно, чтобы вызывающий код мог назначить parent.

Или сделать родительский параметр параметром в функции.

...