亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Delphi > 正文

Delphi的組件讀寫機制(三)

2019-11-18 18:35:27
字體:
來源:轉載
供稿:網友
Ø        TReader
       先來看Delphi的工程文件,會發現類似這樣的幾行代碼:
begin
  application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
       這是Delphi程序的入口。簡單的說一下這幾行代碼的意義:Application.Initialize對開始運行的應用程序進行一些必要的初始化工作,Application.CreateForm(TForm1, Form1)創建必要的窗體,Application.Run程序開始運行,進入消息循環。
       現在我們最關心的是創建窗體這一句。窗體以及窗體上的組件是怎么創建出來的呢?在前面已經提到過:窗體中的所有組件包括窗體自身的屬性都包含在DFM文件中,而Delphi在編譯程序的時候,利用編譯指令{$R *.dfm}已經把DFM文件信息編譯到可執行文件中。因此,可以斷定創建窗體的時候需要去讀取DFM信息,用什么去讀呢,當然是TReader了!
       通過對程序的一步步的跟蹤,可以發現程序在創建窗體的過程中調用了TReader的ReadRootComponent方法。該方法的作用是讀出根組件及其所擁有的全部組件。來看一下該方法的實現:
 
function TReader.ReadRootComponent(Root: TComponent): TComponent;
……
begin
  ReadSignature;
  Result := nil;
  GlobalNameSpace.BeginWrite;  // Loading from stream adds to name space
  try
    try
      ReadPRefix(Flags, I);
      if Root = nil then
      begin
        Result := TComponentClass(FindClass(ReadStr)).Create(nil);
        Result.Name := ReadStr;
      end else
      begin
        Result := Root;
        ReadStr; { Ignore class name }
        if csDesigning in Result.ComponentState then
          ReadStr else
        begin
          Include(Result.FComponentState, csLoading);
          Include(Result.FComponentState, csReading);
          Result.Name := FindUniqueName(ReadStr);
        end;
      end;
      FRoot := Result;
      FFinder := TClassFinder.Create(TPersistentClass(Result.ClassType), True);
      try
        FLookupRoot := Result;
        G := GlobalLoaded;
        if G <> nil then
          FLoaded := G else
          FLoaded := TList.Create;
        try
          if FLoaded.IndexOf(FRoot) < 0 then
            FLoaded.Add(FRoot);
          FOwner := FRoot;
          Include(FRoot.FComponentState, csLoading);
          Include(FRoot.FComponentState, csReading);
          FRoot.ReadState(Self);
          Exclude(FRoot.FComponentState, csReading);
          if G = nil then
            for I := 0 to FLoaded.Count - 1 do TComponent(FLoaded[I]).Loaded;
        finally
          if G = nil then FLoaded.Free;
          FLoaded := nil;
        end;
      finally
        FFinder.Free;
      end;
     ……
  finally
    GlobalNameSpace.EndWrite;
  end;
end;
       ReadRootComponent首先調用ReadSignature讀取Filer對象標簽(’TPF0’)。載入對象之前檢測標簽,能防止疏忽大意,導致讀取無效或過時的數據。
       再看一下ReadPrefix(Flags, I)這一句,ReadPrefix方法的功能與ReadSignature的很相象,只不過它是讀取流中組件前面的標志(PreFix)。當一個Write對象將組件寫入流中時,它在組件前面預寫了兩個值,第一個值是指明組件是否是從祖先窗體中繼承的窗體和它在窗體中的位置是否重要的標志;第二個值指明它在祖先窗體創建次序。
       然后,如果Root參數為nil,則用ReadStr讀出的類名創建新組件,并從流中讀出組件的Name屬性;否則,忽略類名,并判斷Name屬性的唯一性。
          FRoot.ReadState(Self);
       這是很關鍵的一句,ReadState方法讀取根組件的屬性和其擁有的組件。這個ReadState方法雖然是TComponent的方法,但進一步的跟蹤就可以發現,它實際上最終還是定位到了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;
       其中有這樣的這一行代碼:
  while not EndOfList do ReadProperty(Instance);
       這是用來讀取根組件的屬性的,對于屬性,前面提到過,既有組件本身的published屬性,也有非published屬性,例如TTimer的Left和Top。對于這兩種不同的屬性,應該有兩種不同的讀方法,為了驗證這個想法,我們來看一下ReadProperty方法的實現。
procedure TReader.ReadProperty(AInstance: TPersistent);
……
begin
       ……
      PropInfo := GetPropInfo(Instance.ClassInfo, FPropName);
      if PropInfo <> nil then ReadPropValue(Instance, PropInfo) else
      begin
        { Cannot reliably recover from an error in a defined property }
        FCanHandleExcepts := False;
        Instance.DefineProperties(Self);
        FCanHandleExcepts := True;
        if FPropName <> '' then
          PropertyError(FPropName);
      end;
       ……
end;
       為了節省篇幅,省略了一些代碼,這里說明一下:FPropName是從文件讀取到的屬性名。
      PropInfo := GetPropInfo(Instance.ClassInfo, FPropName);
       這一句代碼是獲得published屬性FPropName的信息。從接下來的代碼中可以看到,如果屬性信息不為空,就通過ReadPropValue方法讀取屬性值,而ReadPropValue方法是通過RTTI函數來讀取屬性值的,這里不再詳細介紹。如果屬性信息為空,說明屬性FPropName為非published的,它就必須通過另外一種機制去讀取。這就是前面提到的DefineProperties方法,如下:
       Instance.DefineProperties(Self);
       該方法實際上調用的是TReader的DefineProperty方法:
procedure TReader.DefineProperty(const Name: string;
  ReadData: TReaderProc; WriteData: TWriterProc; HasData: Boolean);
begin
  if SameText(Name, FPropName) and Assigned(ReadData) then
  begin
    ReadData(Self);
    FPropName := '';
  end;
end;
       它先去比較讀取的屬性名是否和預設的屬性名相同,如果相同并且讀方法ReadData不為空時就調用ReadData方法讀取屬性值。
       好了,根組件已經讀上來了,接下來應該是讀該根組件所擁有的組件了。再來看方法:
procedure TReader.ReadDataInner(Instance: TComponent);
       該方法后面有一句這樣的代碼:
    while not EndOfList do ReadComponent(nil);
       這正是用來讀取子組件的。子組件的讀取機制是和上面所介紹的根組件的讀取一樣的,這是一個樹的深度遍歷。
       到這里為止,組件的讀機制已經介紹完了。
 
       再來看組件的寫機制。當我們在窗體上添加一個組件時,它的相關的屬性就會保存在DFM文件中,這個過程就是由TWriter來完成的。
 
Ø        TWriter
       TWriter 對象是可實例化的往流中寫數據的Filer對象。TWriter對象直接從TFiler繼承而來,除了覆蓋從TFiler繼承的方法外,還增加了大量的關于寫各種數據類型(如Integer、String和Component等)的方法。
       TWriter對象提供了許多往流中寫各種類型數據的方法, TWrite對象往流中寫數據是依據不同的數據采取不同的格式的。 因此要掌握TWriter對象的實現和應用方法,必須了解Writer對象存儲數據的格式。
  首先要說明的是,每個Filer對象的流中都包含有Filer對象標簽。該標簽占四個字節其值為“TPF0”。Filer對象為WriteSignature和ReadSignature方法存取該標簽。該標簽主要用于Reader對象讀數據(組件等)時,指導讀操作。
  其次,Writer對象在存儲數據前都要留一個字節的標志位,以指出后面存放的是什么類型的數據。該字節為TValueType類型的值。TValueType是枚舉類型,占一個字節空間,其定義如下:
 
  TValueType = (VaNull, VaList, VaInt8, VaInt16, VaInt32, VaEntended, VaString, VaIdent,
VaFalse, VaTrue, VaBinary, VaSet, VaLString, VaNil, VaCollection);
 
       因此,對Writer對象的每一個寫數據方法,在實現上,都要先寫標志位再寫相應的數據;而Reader對象的每一個讀數據方法都要先讀標志位進行判斷,如果符合就讀數據,否則產生一個讀數據無效的異常事件。VaList標志有著特殊的用途,它是用來標識后面將有一連串類型相同的項目,而標識連續項目結束的標志是VaNull。因此,在Writer對象寫連續若干個相同項目時,先用WriteListBegin寫入VaList標志,寫完數據項目后,再寫出VaNull標志;而讀這些數據時,以ReadListBegin開始,ReadListEnd結束,中間用EndofList函數判斷是否有VaNull標志。
       來看一下TWriter的一個非常重要的方法WriteData:
procedure TWriter.WriteData(Instance: TComponent);
……
begin
  ……
  WritePrefix(Flags, FChildPos);
  if UseQualifiedNames then
    WriteStr(GetTypeData(PTypeInfo(Instance.ClassType.ClassInfo)).UnitName + '.' + Instance.ClassName)
  else
    WriteStr(Instance.ClassName);
  WriteStr(Instance.Name);
  PropertiesPosition := Position;
  if (FAncestorList <> nil) and (FAncestorPos < FAncestorList.Count) then
  begin
    if Ancestor <> nil then Inc(FAncestorPos);
    Inc(FChildPos);
  end;
  WriteProperties(Instance);
  WriteListEnd;
  ……
end;
       從WriteData方法中我們可以看出生成DFM文件信息的概貌。先寫入組件前面的標志(PreFix),然后寫入類名、實例名。緊接著有這樣的一條語句:
  WriteProperties(Instance);
       這是用來寫組件的屬性的。前面提到過,在DFM文件中,既有published屬性,又有非published屬性,這兩種屬性的寫入方法應該是不一樣的。來看WriteProperties的實現:
procedure TWriter.WriteProperties(Instance: TPersistent);
……
begin
  Count := GetTypeData(Instance.ClassInfo)^.PropCount;
  if Count > 0 then
  begin
    GetMem(PropList, Count * SizeOf(Pointer));
    try
      GetPropInfos(Instance.ClassInfo, PropList);
      for I := 0 to Count - 1 do
      begin
        PropInfo := PropList^[I];
        if PropInfo = nil then
          Break;
        if IsStoredProp(Instance, PropInfo) then
          WriteProperty(Instance, PropInfo);
      end;
    finally
      FreeMem(PropList, Count * SizeOf(Pointer));
    end;
  end;
  Instance.DefineProperties(Self);
end;
       請看下面的代碼:
        if IsStoredProp(Instance, PropInfo) then
          WriteProperty(Instance, PropInfo);
       函數IsStoredProp通過存儲限定符來判斷該屬性是否需要保存,如需保存,就調用WriteProperty來保存屬性,而WriteProperty是通過一系列的RTTI函數來實現的。
       Published屬性保存完后就要保存非published屬性了,這是通過這句代碼完成的:
  Instance.DefineProperties(Self);
       DefineProperties的實現前面已經講過了,TTimer的Left、Top屬性就是通過它來保存的。
       好,到目前為止還存在這樣的一個疑問:根組件所擁有的子組件是怎么保存的?再來看WriteData方法(該方法在前面提到過):
procedure TWriter.WriteData(Instance: TComponent);
……
begin
  ……
    if not IgnoreChildren then
      try
        if (FAncestor <> nil) and (FAncestor is TComponent) then
        begin
          if (FAncestor is TComponent) and (csInline in TComponent(FAncestor).ComponentState) then
            FRootAncestor := TComponent(FAncestor);
          FAncestorList := TList.Create;
          TComponent(FAncestor).GetChildren(AddAncestor, FRootAncestor);
        end;
        if csInline in Instance.ComponentState then
          FRoot := Instance;
        Instance.GetChildren(WriteComponent, FRoot);
      finally
        FAncestorList.Free;
      end;
end;
       IgnoreChildren屬性使一個Writer對象存儲組件時可以不存儲該組件擁有的子組件。如果IgnoreChildren屬性為True,則Writer對象存儲組件時不存它擁有的子組件。否則就要存儲子組件。
        Instance.GetChildren(WriteComponent, FRoot);
       這是寫子組件的最關鍵的一句,它把WriteComponent方法作為回調函數,按照深度優先遍歷樹的原則,如果根組件FRoot存在子組件,則用WriteComponent來保存它的子組件。這樣我們在DFM文件中看到的是樹狀的組件結構。

上一篇:Delphi的組件讀寫機制(一)

下一篇:Delphi中如何編寫圖像解析組件

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
學習交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網友關注

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品美女免费| 97在线观看免费| 欧美精品做受xxx性少妇| 精品久久久久久| 亚洲人成电影网站色www| 亚洲国产成人久久综合一区| 在线日韩中文字幕| 欧美激情视频在线观看| 91精品国产高清久久久久久| 日韩精品免费在线视频| 亚洲深夜福利视频| 国产精品一区二区三区久久| 欧美亚洲成人免费| 国产精品亚洲美女av网站| 亚洲欧美国产va在线影院| 91精品国产777在线观看| 色噜噜狠狠狠综合曰曰曰| 日韩欧美综合在线视频| 伊人伊成久久人综合网小说| 国产欧美婷婷中文| 欧美另类69精品久久久久9999| 成人h视频在线观看播放| 中文字幕亚洲字幕| 午夜精品一区二区三区在线视| 尤物精品国产第一福利三区| 亚洲男人av电影| 欧美日韩中文在线观看| 亚洲欧美国产另类| 91久久精品国产91久久性色| 一区二区欧美激情| 欧美在线欧美在线| 精品亚洲男同gayvideo网站| 隔壁老王国产在线精品| 亚洲视频免费一区| 91成人性视频| 美女视频久久黄| 欧美丰满少妇xxxxx做受| 亚洲性猛交xxxxwww| 福利一区福利二区微拍刺激| 国语自产精品视频在线看抢先版图片| 欧美另类在线播放| 国产久一一精品| 7777kkkk成人观看| 久久精品国产久精国产思思| 久久综合伊人77777| 福利视频第一区| 97精品国产97久久久久久| 亚洲3p在线观看| 综合136福利视频在线| 国产精品中文字幕久久久| 免费97视频在线精品国自产拍| 热re91久久精品国99热蜜臀| 夜夜嗨av一区二区三区四区| 欧美日产国产成人免费图片| 亚洲亚裔videos黑人hd| 欧美精品aaa| 国产精品va在线播放我和闺蜜| 国产精品久久久久久久久粉嫩av| 中文字幕日韩电影| 国产区精品视频| 日韩一区二区三区xxxx| 精品视频9999| 亚洲男人天堂视频| 尤物99国产成人精品视频| 77777亚洲午夜久久多人| 在线观看精品自拍私拍| 青青青国产精品一区二区| 欧美成人午夜剧场免费观看| 国模精品系列视频| 91精品国产综合久久久久久蜜臀| 日韩高清免费在线| 亚洲高清一二三区| 成人性教育视频在线观看| 777国产偷窥盗摄精品视频| 91人人爽人人爽人人精88v| 欧美肥婆姓交大片| 九九精品在线观看| 欧美激情在线观看| 国产偷亚洲偷欧美偷精品| 亚洲成人在线视频播放| 欧美激情va永久在线播放| 久久精品国产成人精品| 久久精品在线播放| 欧美成人在线免费| 国产成人精品一区二区| 久久这里有精品视频| 97成人精品区在线播放| 一本色道久久综合狠狠躁篇的优点| 欧美黑人性生活视频| 国产va免费精品高清在线观看| 精品调教chinesegay| 欧美诱惑福利视频| 国产福利精品在线| 国产欧美精品在线| 国产精品扒开腿做爽爽爽男男| 亚洲三级黄色在线观看| 欧美日韩在线免费观看| 久久最新资源网| 国产日本欧美一区| 亚洲白拍色综合图区| 国语自产精品视频在线看抢先版图片| 麻豆乱码国产一区二区三区| 欧美精品制服第一页| 欧美国产日韩视频| 欧美日韩中文字幕综合视频| 欧美视频不卡中文| 日韩中文字幕在线视频| 一区二区三区回区在观看免费视频| 日韩成人av在线播放| 欧美亚洲日本黄色| 国产va免费精品高清在线观看| zzjj国产精品一区二区| 欧美激情aaaa| 97精品国产97久久久久久免费| 国产精品欧美日韩| 91精品视频在线| 日本精品视频在线| 日韩精品久久久久久福利| 国产狼人综合免费视频| 色综合男人天堂| 亚洲精品456在线播放狼人| 色哟哟亚洲精品一区二区| 欧美一级大片在线观看| 国产精品18久久久久久麻辣| 69久久夜色精品国产69乱青草| 国产精品视频一区国模私拍| 国产精品久久久久久av福利| 在线国产精品视频| 国产精品91在线| 国产视频精品自拍| 欧美精品在线看| 国产日韩欧美夫妻视频在线观看| 国产美女扒开尿口久久久| 欧美日韩免费在线观看| 亚洲精品www久久久久久广东| 黑人欧美xxxx| 国产精品盗摄久久久| 国产亚洲精品一区二区| 国产精品久久电影观看| 亚洲一区二区在线播放| 欧美日韩爱爱视频| 国产成人精品最新| 国产精品电影观看| 日韩欧美视频一区二区三区| 精品在线欧美视频| 国产有码一区二区| 色综合久久88色综合天天看泰| 中文字幕自拍vr一区二区三区| 亚洲综合在线中文字幕| 欧美一级高清免费播放| 亚洲淫片在线视频| 日韩av在线最新| 成人夜晚看av| 色婷婷综合成人| 91av在线不卡| 欧美精品成人91久久久久久久| 亚洲成人在线视频播放| 精品国产拍在线观看| 亚洲人永久免费| 亚洲第一精品福利| 国产精品96久久久久久又黄又硬| 亚洲aⅴ男人的天堂在线观看| 亚洲国产精品成人va在线观看| 国产精品成人一区二区三区吃奶|