Элемент управления "xxx" не имеет родительского окна

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

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

спасибо!

6 ответов


об ошибке

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

это не то, почему появляется сообщение об ошибке. Вы видите это сообщение об ошибке, потому что для некоторых кодов, используемых для настройки фрейма, требуется дескриптор окна для некоторой операции. Это может быть что угодно, например, установка заголовка некоторого компонента (что внутренне требует дескриптора окна для некоторых вычислений). Лично я ненавижу, когда такое случается. Когда я создаю GUI из кода I старайтесь как можно дольше оттягивать назначение родителя, в попытке оттянуть создание окна, чтобы меня это много раз укусило.

специфичный для вашего использования DLL, возможное исправление

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

function GiveMeTheNiceFrame:HWND;

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

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

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

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


есть несколько важных вещей, чтобы помнить:

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

попробуйте воспроизвести свою проблему внутри EXE; если вы не можете воспроизвести, это, вероятно, первое, что указано выше список.

--jeroen


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

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

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

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


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

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

И Приложения.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;

Теперь это просто бросает ошибки при попытке установить фокус на subcontrols, которые, я думаю, я исправлю, перехватывая 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;

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

SomeControl.Parent := nil;//Before free your TControl
SomeControl.Free;

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

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