比如在工程文件中:有如下两句:
//...
Application.CreateForm (TForm1, Form1);
Application.CreateForm (TForm2, Form2);
//....
如果一开始就把Form1, Form2的Visible属性设为true
不必等到Application.Run;两个窗口都会显示出来:
Form1我可以理解:毕竟它是主窗体,我觉得其建立(指WinApi:CreateWindow)是在
HandleNeeded中。
但是Form2怎么会建立,我不明白,因为Form2是执行不到HandleNeeded这个方法的。小弟才学Delphi,希望各位大哥不吝赐教。
//...
Application.CreateForm (TForm1, Form1);
Application.CreateForm (TForm2, Form2);
//....
如果一开始就把Form1, Form2的Visible属性设为true
不必等到Application.Run;两个窗口都会显示出来:
Form1我可以理解:毕竟它是主窗体,我觉得其建立(指WinApi:CreateWindow)是在
HandleNeeded中。
但是Form2怎么会建立,我不明白,因为Form2是执行不到HandleNeeded这个方法的。小弟才学Delphi,希望各位大哥不吝赐教。
先谢过了
procedure TApplication.CreateForm(InstanceClass:TComponentClass;var Reference);
var
Instance:TComponent;
begin
Instance:=TComponent(InstanceClass.NewInstance);
......//创建窗体实例的代码省略
//第一个创建的窗体实例就是MainForm
if (FMainForm=Nil) and (Instance is TForm) then
begin
TForm(Instance).HandleNeeded;
FMainForm:=TForm(Instance);
end;
end;
。
看看上面的注释,说明窗体实例的创建不是在HandleNeeded中完成的!!!!不过具体HandleNeeded做了什么,我也说不清楚,还是等其他高人来解答吧......呵呵,俺现在给你找高人去,呵呵...闪!
var
Instance: TComponent;
begin
Instance := TComponent(InstanceClass.NewInstance);
TComponent(Reference) := Instance;
try
Instance.Create(Self);
except
TComponent(Reference) := nil;
raise;
end;
if (FMainForm = nil) and (Instance is TForm) then
begin
TForm(Instance).HandleNeeded;
FMainForm := TForm(Instance);
end;
end;实际上窗体的创建在Instance.Create(Self);这一步完成的HandleNeeded是当取
Form的Handle是调用的
property Handle: HWnd read GetHandle;function TWinControl.GetHandle: HWnd;
begin
HandleNeeded;
Result := FHandle;
end;而取Handle的这些在Instance.Create(Self);
就会去做
http://expert.csdn.net/Expert/topic/1604/1604705.xml?temp=.3986933
begin
FComponentStyle := [csInheritable];
if AOwner <> nil then AOwner.InsertComponent(Self);
end;
你认为是在这里创建的!?可是我看到上面这个过程中没有具体的创建动作啊?只是设置属性和添加列表啊!
应该是TCustomForm.Createconstructor TCustomForm.Create(AOwner: TComponent);
begin
GlobalNameSpace.BeginWrite;
try
CreateNew(AOwner);
if (ClassType <> TForm) and not (csDesigning in ComponentState) then
begin
Include(FFormState, fsCreating);
try
if not InitInheritedComponent(Self, TForm) then
raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
finally
Exclude(FFormState, fsCreating);
end;
if OldCreateOrder then DoCreate;
end;
finally
GlobalNameSpace.EndWrite;
end;
end;
1。Instance:=TComponent(InstanceClass.NewInstance);
是为这个对象分配内存,和窗口的建立根本没有关系,而且这个Instance是TForm类型(具体怎么回事我不清楚)。
2。因为Instance是TForm类型,所以Create是调用的TCustomForm的,但是我找遍构造函数,找不到CreateWndEx,事实上我猜测也不可能是在构造函数。
3。HandleNeeded中我可以间接找到CreateWndEx,大家自己跟到VCL源代码去看好了,很容易。
4。Application.Run跟窗口的显示是有关系的,因为主窗体最初是设为不可见的,就是在这个函数中设为可见。
Form2的CreateWndEx肯定在什么地方,我只是找不到,希望有高人能找到。
我只是一个计算机系的学生,谢谢大家的关怀。
我重新贴上来吧
希望对你有所帮助我从
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
一开始调试
记录一下主要的过程
当Application.CreateForm时
执行到
constructor TCustomForm.Create(AOwner: TComponent);
begin
GlobalNameSpace.BeginWrite;
try
CreateNew(AOwner);
if (ClassType <> TForm) and not (csDesigning in ComponentState) then
begin
Include(FFormState, fsCreating);
try
if not InitInheritedComponent(Self, TForm) then
raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
finally
Exclude(FFormState, fsCreating);
end;
if OldCreateOrder then DoCreate;
end;
finally
GlobalNameSpace.EndWrite;
end;
end;关键一处
InitInheritedComponent
由调试看出的调用顺序,关键的部分InitInheritedComponent
InternalReadComponentRes //读取窗体资源,也就是准备根据DFM描述建立窗体控件
TResourceStream.ReadComponent
TReader.ReadRootComponent
TCustomForm.ReadState
TComponent.ReadState
TReader.ReadData
TReader.ReadDataInner
这个比较让人感兴趣的
procedure TReader.ReadDataInner(Instance: TComponent);
var
OldParent, OldOwner: TComponent;
begin
while not EndOfList do ReadProperty(Instance);
ReadListEnd;
OldParent := Parent;
OldOwner := Owner;
Parent := Instance.GetChildParent;
try
Owner := Instance.GetChildOwner;
if not Assigned(Owner) then Owner := Root;
while not EndOfList do ReadComponent(nil);
ReadListEnd;
finally
Parent := OldParent;
Owner := OldOwner;
end;
end;
这句ReadProperty(Instance);就是读取MainForm上的属性并设置属性值
ReadComponent就是读取MainForm上的控件描述并建立之并设置相应的控件属性值
这些操作做完之后,回到
procedure TCustomForm.ReadState(Reader: TReader);
var
NewTextHeight: Integer;
Scaled: Boolean;
begin
DisableAlign;
try
FClientWidth := 0;
FClientHeight := 0;
FTextHeight := 0;
Scaled := False;
FOldCreateOrder := not ModuleIsCpp;
inherited ReadState(Reader); //回到这 if (FPixelsPerInch <> 0) and (FTextHeight > 0) then
begin
if (sfFont in ScalingFlags) and (FPixelsPerInch <> Screen.PixelsPerInch) then
Font.Height := MulDiv(Font.Height, Screen.PixelsPerInch, FPixelsPerInch);
FPixelsPerInch := Screen.PixelsPerInch;
NewTextHeight := GetTextHeight;
if FTextHeight <> NewTextHeight then
begin
Scaled := True;
ScaleScrollBars(NewTextHeight, FTextHeight);
ScaleControls(NewTextHeight, FTextHeight);
if sfWidth in ScalingFlags then
FClientWidth := MulDiv(FClientWidth, NewTextHeight, FTextHeight);
if sfHeight in ScalingFlags then
FClientHeight := MulDiv(FClientHeight, NewTextHeight, FTextHeight);
if sfDesignSize in ScalingFlags then
begin
FDesignSize.X := MulDiv(FDesignSize.X, NewTextHeight, FTextHeight);
FDesignSize.Y := MulDiv(FDesignSize.Y, NewTextHeight, FTextHeight);
end;
end;
end;
if FClientWidth > 0 then inherited ClientWidth := FClientWidth;
if FClientHeight > 0 then inherited ClientHeight := FClientHeight;
ScalingFlags := [];
if not Scaled then
begin
{ Forces all ScalingFlags to [] }
ScaleScrollBars(1, 1);
ScaleControls(1, 1);
end;
Perform(CM_PARENTBIDIMODECHANGED, 0, 0);
finally
EnableAlign;
end;
end;注意:我这边调试了好几遍窗体的创建就在这个属性设置
NewTextHeight := GetTextHeight;调用GetTextHeight,跟踪
function TCustomForm.GetTextHeight: Integer;
begin
Result := Canvas.TextHeight('0');
end;
到达
function TCanvas.TextHeight(const Text: string): Integer;
begin
Result := TextExtent(Text).cY;
end;function TCanvas.TextExtent(const Text: string): TSize;
begin
RequiredState([csHandleValid, csFontValid]);
Result.cX := 0;
Result.cY := 0;
Windows.GetTextExtentPoint32(FHandle, PChar(Text), Length(Text), Result);
end;procedure TCanvas.RequiredState(ReqState: TCanvasState);
var
NeededState: TCanvasState;
begin
NeededState := ReqState - State;
if NeededState <> [] then
begin
if csHandleValid in NeededState then
begin
CreateHandle;
if FHandle = 0 then
raise EInvalidOperation.CreateRes(@SNoCanvasHandle);
end;
if csFontValid in NeededState then CreateFont;
if csPenValid in NeededState then CreatePen;
if csBrushValid in NeededState then CreateBrush;
State := State + NeededState;
end;
end;好了,主角慢慢快出来了
由于NeededState包含csHandleValid一句所以
procedure TControlCanvas.CreateHandle;
begin
if FControl = nil then inherited CreateHandle else
begin
if FDeviceContext = 0 then
begin
with CanvasList.LockList do
try
if Count >= CanvasListCacheSize then FreeDeviceContext;
FDeviceContext := FControl.GetDeviceContext(FWindowHandle);
Add(Self);
finally
CanvasList.UnlockList;
end;
end;
Handle := FDeviceContext;
UpdateTextFlags;
end;
end;
继续执行到了
function TWinControl.GetDeviceContext(var WindowHandle: HWnd): HDC;
begin
if csDesigning in ComponentState then
Result := GetDCEx(Handle, 0, DCX_CACHE or DCX_CLIPSIBLINGS)
else
Result := GetDC(Handle);
if Result = 0 then raise EOutOfResources.CreateRes(@SWindowDCError);
WindowHandle := FHandle;
end;执行到了
Result := GetDC(Handle);
Handle为TWinControl的属性
这样定义的
property Handle: HWnd read GetHandle;
所以
function TWinControl.GetHandle: HWnd;
begin
HandleNeeded;
Result := FHandle;
end;procedure TWinControl.HandleNeeded;
begin
if FHandle = 0 then
begin
if Parent <> nil then Parent.HandleNeeded;
CreateHandle;
end;
end;而这时窗体没有创建FHandle = 0;
执行到CreateHandleprocedure TWinControl.CreateHandle;
var
I: Integer;
begin
if FHandle = 0 then
begin
CreateWnd;
SetProp(FHandle, MakeIntAtom(ControlAtom), THandle(Self));
SetProp(FHandle, MakeIntAtom(WindowAtom), THandle(Self));
if Parent <> nil then
SetWindowPos(FHandle, Parent.PrecedingWindow(Self), 0, 0, 0, 0,
SWP_NOMOVE + SWP_NOSIZE + SWP_NOACTIVATE);
for I := 0 to ControlCount - 1 do
Controls[I].UpdateAnchorRules;
end;
end;