2010-09-23 17 views
9

Sto cercavo di scrivere una libreria DLL in Delphi wih una funzione che crea un'istanza di un discendente TFrame e lo restituisce. Ma quando ho importato questa funzione in un'applicazione, ogni volta che l'ho chiamata otterrei un'eccezione come "il controllo 'xxx' non ha una finestra genitore". Non sono sicuro al 100%, ma l'eccezione apparve nel costruttore di quella classe quando si accedeva a uno dei controlli della GUI.Il controllo 'xxx' non ha alcuna finestra padre

Potresti dirmi qual è la ragione di tale comportamento? Dovrei semplicemente usare i discendenti TForm o esiste una soluzione migliore?

Grazie!

+0

Perché si utilizzano DLL e non pacchetti? Le DLL sono un incubo per avere ragione. –

risposta

8

sull'errore

Questo messaggio di errore viene generato dall'unità Controls.pas, dal metodo TWinControl.CreateWnd. Essenzialmente quel codice è usato per creare l'handle di Window per il tuo discendente TWinControl (TFrame, TButton, TEdit ... se può avere il focus della tastiera è un discendente TWinControl), ed è in realtà un messaggio di errore molto sensato: non puoi avere un Finestra senza un WindowParent, e visto che stiamo parlando della VCL qui, ha molto senso cercare di ottenere l'handle della finestra genitore da TWinControl.Parent; E questo non è assegnato.

Questo non è PERCHÉ il messaggio di errore sta spuntando. Puoi vedere quel messaggio di errore perché parte del codice che stai usando per configurare il frame richiede un handle di Window per alcune operazioni. Potrebbe essere qualsiasi cosa, come impostare la didascalia di alcuni componenti (che internamente richiede un handle di finestra per alcuni calcoli). Personalmente lo odio davvero quando ciò accade.Quando creo le GUI dal codice, cerco di ritardare il più possibile l'assegnazione di Parent, nel tentativo di ritardare la creazione della finestra, così sono stato morso da molte altre volte.

specifico per l'utilizzo DLL, possibile correzione

ho intenzione di mettere il mio cappello psico lettore di mente su. Dal momento che è necessario restituire un FRAME dalla DLL e non è possibile restituire il Frame effettivo perché si tratta di un oggetto specifico di Delphi e non è possibile restituire oggetti specifici di Delphi oltre i limiti della DLL, la mia ipotesi è che si sta tornando una maniglia della finestra, così come tutte la bella API, utilizzando una definizione di funzione come questa:

function GiveMeTheNiceFrame:HWND; 

il guaio è, che di routine richiede la creazione di l'handle di finestra reale, da una chiamata a TWinControl.CreateWnd, e, a sua volta, che la chiamata richiede un handle della finestra genitore per impostare la chiamata a Windows.CreateWindowEx e la routine non può ottenere un handle della finestra genitore, quindi si verifica un errore.

provare a sostituire la vostra funzione con qualcosa allong le linee di:

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

... cioè: utilizzare il CreateParented(AParentWindow:HWND) costruttore, non il solito Create(AOwner:TComponent) e superare un HWND proprietario alla DLL.

+0

Grazie per la risposta. Penso che dovrebbe aiutare; Ci proverò più tardi. A proposito, lascia che ti faccia una domanda. Perché non è consentito restituire un oggetto specifico Delphi su DLL? Userò questa libreria solo con le applicazioni Delphi. La funzione restituisce il tipo TFrame ma l'oggetto reale creato è un'istanza di una classe discendente. Non va bene? Grazie! –

+1

Non va bene per ragioni di versione: l'utilizzo dell'oggetto restituito richiede la conoscenza del suo layout di memoria, e questo è soggetto a modifiche (può cambiare con la versione di Delphi).Funzionerà quando lo testerete, e funzionerà se Exe e Dll sono costruiti con lo stesso compilatore e le stesse impostazioni del compilatore, ma se imporrete tali restrizioni, potreste anche usare i pacchetti. –

+0

Grazie, è pazzesco saperlo. Ho fatto quello che mi hai suggerito e sto ancora ricevendo l'eccezione :-(. La funzione esportata fa questo: Risultato: = TFrame1.CreateParented (ParentWindowHandle); ParentWindowHandle è un handle della finestra principale di un'app che chiama il metodo (Self.Handle, quando chiamato dalla classe della finestra principale) –

0

Suona come è sufficiente assegnare il componente (una forma o una parte di una forma, come un pannello) che contiene la cornice theframe.parent.

Non è possibile eseguire la GUI prima che sia stata assegnata. I frame sono parti di moduli da riutilizzare e normalmente devono essere assegnati ad alcuni genitori.

Spostare il codice della GUI su onshow o una procedura che si chiama esplicitamente, in modo che il codice chiamante possa assegnare il genitore.

Oppure impostare come parametro un genitore nella funzione.

+0

Dopo aver letto la tua risposta (sono un po 'cieco), sembra che la tua risposta manchi un po' dopo che "normalmente è necessario" ... –

+0

corretto. Scrivere in fretta è sempre un problema per me. Lavorare su tutte le frasi contemporaneamente :) –

1

Ci sono alcune cose importanti da ricordare:

  1. Quando si utilizzano le DLL, sia la DLL e il vostro EXE hanno ciascuno un'istanza di applicazione che stanno lottando per il controllo. I controlli nella DLL vedranno l'istanza dell'applicazione che appartiene alla DLL; i controlli nel tuo EXE vedranno l'istanza dell'applicazione che appartiene all'EXE. Quella lotta non è lì quando si usano i pacchetti, poiché ci sarà solo un'istanza di applicazione.
  2. I frame sono controlli, ma non sono moduli.
  3. Quando si utilizzano i controlli in un'applicazione, non possono esistere visivamente senza un controllo padre (di solito un modulo o un contenitore che ha una gerarchia padre verso un modulo).
  4. Alcuni controlli non possono esporre la loro piena funzionalità a meno che non esistano visivamente e abbiano un genitore valido.

Cercare di riprodurre il problema all'interno dell'EXE; se non riesci a riprodurlo, probabilmente è la prima cosa nella lista di cui sopra.

--jeroen

0

ho trovato questo (CreateParams si chiama come parte di CreateWnd):

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

E Application.Handle = 0 in modo che getta sempre l'errore più avanti in CreateWnd.
Dopo aver letto questo Delphi: How to call inherited inherited ancestor on a virtual method?

ho risolto ridefinendo CreateParams nel mio telaio perdere la versione 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; 

ora è solo gettare gli errori quando si cerca di impostare la messa a fuoco subcontrols, che Penso che risolverò intercettando WM_FOCUS ma vedremo come andrà da qui.

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

È possibile evitare questo messaggio assegnando nil all'evento OnClose genitore, a volte funziona:

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

Questa è una programmazione accidentale. – Ampere

+0

Kachwahed: non dovresti programmare per congettura. Sarà solo sporcare il tuo codice con spazzatura inutile che di solito funge da catalizzatore per bug sottili e inaspettati. Sì, può essere difficile; ma è necessario mettere lo sforzo nell'analisi delle cause principali in modo da poter comprendere correttamente il problema. In questo caso particolare, la tua risposta è totalmente irrilevante per il problema dell'OP. –

+0

E come secondo punto, non dovresti cercare di "evitare i messaggi di errore". I messaggi di errore non sono un _problem_ ... Sono semplicemente un *** messaggio *** su un problema. Quindi non sparare semplicemente al messaggero. Scopri che cos'è _ il problema reale_ e ** correggi il problema * reale * **! –

0

Penso che questa sia una soluzione molto cool. Penso che non sia provato prima :) Sto usando un genitore fittizio (che è un modulo).

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 
Problemi correlati