NativeXmlObjectStorage.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:39k
源码类别:

Email服务器

开发平台:

Delphi

  1. { unit NativeXmlObjectStorage
  2.   This unit provides functionality to store any TObject descendant to an XML file
  3.   or stream. Internally it makes full use of RTTI (runtime type information) in
  4.   order to store all published properties and events.
  5.   It can even be used to copy forms, but form inheritance is not exploited, so
  6.   child forms descending from parent forms store everything that the parent already
  7.   stored.
  8.   All published properties and events of objects are stored. This includes
  9.   the "DefineProperties". These are stored in binary form in the XML, encoded
  10.   as BASE64.
  11.   Known limitations:
  12.   - The method and event lookup will not work correctly across forms.
  13.   Please see the "ObjectToXML" demo for example usage of this unit.
  14.   Copyright (c) 2004 - 2006 Simdesign B.V., Author Nils Haeck M.Sc.
  15.   It is NOT allowed under ANY circumstances to publish or copy this code
  16.   without prior written permission of the Author!
  17.   This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
  18.   ANY KIND, either express or implied.
  19.   Please visit http://www.simdesign.nl/xml.html for more information.
  20. }
  21. // Delphi and BCB versions
  22. // Delphi 3
  23. {$IFDEF VER110}
  24.   {$DEFINE D3UP}
  25. {$ENDIF}
  26. // Delphi 4
  27. {$IFDEF VER120}
  28.   {$DEFINE D3UP}
  29.   {$DEFINE D4UP}
  30. {$ENDIF}
  31. // BCB 4
  32. {$IFDEF VER125}
  33.   {$DEFINE D4UP}
  34. {$ENDIF}
  35. // Delphi 5
  36. {$IFDEF VER130}
  37.   {$DEFINE D3UP}
  38.   {$DEFINE D4UP}
  39.   {$DEFINE D5UP}
  40. {$ENDIF}
  41. //Delphi 6
  42. {$IFDEF VER140}
  43.   {$DEFINE D3UP}
  44.   {$DEFINE D4UP}
  45.   {$DEFINE D5UP}
  46.   {$DEFINE D6UP}
  47. {$ENDIF}
  48. //Delphi 7
  49. {$IFDEF VER150}
  50.   {$DEFINE D3UP}
  51.   {$DEFINE D4UP}
  52.   {$DEFINE D5UP}
  53.   {$DEFINE D6UP}
  54.   {$DEFINE D7UP}
  55. {$ENDIF}
  56. //Delphi 8
  57. {$IFDEF VER160}
  58.   {$DEFINE D3UP}
  59.   {$DEFINE D4UP}
  60.   {$DEFINE D5UP}
  61.   {$DEFINE D6UP}
  62.   {$DEFINE D7UP}
  63.   {$DEFINE D8UP}
  64. {$ENDIF}
  65. // Delphi 2005
  66. {$IFDEF VER170}
  67.   {$DEFINE D3UP}
  68.   {$DEFINE D4UP}
  69.   {$DEFINE D5UP}
  70.   {$DEFINE D6UP}
  71.   {$DEFINE D7UP}
  72.   {$DEFINE D8UP}
  73.   {$DEFINE D9UP}
  74. {$ENDIF}
  75. // above Delphi 2005
  76. {$IFDEF VER180}
  77.   {$DEFINE D3UP}
  78.   {$DEFINE D4UP}
  79.   {$DEFINE D5UP}
  80.   {$DEFINE D6UP}
  81.   {$DEFINE D7UP}
  82.   {$DEFINE D8UP}
  83.   {$DEFINE D9UP}
  84.   {$DEFINE D10UP}
  85. {$ENDIF}
  86. unit NativeXmlObjectStorage;
  87. interface
  88. uses
  89.   Classes, Forms, SysUtils, Controls, NativeXml, TypInfo
  90.   {$IFDEF D6UP}
  91.   , Variants
  92.   {$ENDIF};
  93. type
  94.   // Use TsdXmlObjectWriter to write any TPersistent descendant's published properties
  95.   // to an XML node.
  96.   TsdXmlObjectWriter = class(TPersistent)
  97.   protected
  98.     procedure WriteProperty(ANode: TXmlNode; AObject: TObject; AParent: TComponent; PropInfo: PPropInfo);
  99.   public
  100.     // Call WriteObject to write the published properties of AObject to the TXmlNode
  101.     // ANode. Specify AParent in order to store references to parent methods and
  102.     // events correctly.
  103.     procedure WriteObject(ANode: TXmlNode; AObject: TObject; AParent: TComponent = nil);
  104.     // Call WriteComponent to write the published properties of AComponent to the TXmlNode
  105.     // ANode. Specify AParent in order to store references to parent methods and
  106.     // events correctly.
  107.     procedure WriteComponent(ANode: TXmlNode; AComponent: TComponent; AParent: TComponent = nil);
  108.   end;
  109.   // Use TsdXmlObjectReader to read any TPersistent descendant's published properties
  110.   // from an XML node.
  111.   TsdXmlObjectReader = class(TPersistent)
  112.   protected
  113.     procedure ReadProperty(ANode: TXmlNode; AObject: TObject; AParent: TComponent; PropInfo: PPropInfo);
  114.   public
  115.     // Call CreateComponent to first create AComponent and then read its published
  116.     // properties from the TXmlNode ANode. Specify AParent in order to resolve
  117.     // references to parent methods and events correctly. In order to successfully
  118.     // create the component from scratch, the component's class must be registered
  119.     // beforehand with a call to RegisterClass. Specify Owner to add the component
  120.     // as a child to Owner's component list. This is usually a form. Specify Name
  121.     // as the new component name for the created component.
  122.     function CreateComponent(ANode: TXmlNode; AOwner, AParent: TComponent; AName: string = ''): TComponent;
  123.     // Call ReadObject to read the published properties of AObject from the TXmlNode
  124.     // ANode. Specify AParent in order to resolve references to parent methods and
  125.     // events correctly.
  126.     procedure ReadObject(ANode: TXmlNode; AObject: TObject; AParent: TComponent = nil);
  127.     // Call ReadComponent to read the published properties of AComponent from the TXmlNode
  128.     // ANode. Specify AParent in order to resolve references to parent methods and
  129.     // events correctly.
  130.     procedure ReadComponent(ANode: TXmlNode; AComponent: TComponent; AParent: TComponent);
  131.   end;
  132. // High-level create methods
  133. // Create and read a component from the XML file with FileName. In order to successfully
  134. // create the component from scratch, the component's class must be registered
  135. // beforehand with a call to RegisterClass. Specify Owner to add the component
  136. // as a child to Owner's component list. This is usually a form. Specify Name
  137. // as the new component name for the created component.
  138. function ComponentCreateFromXmlFile(const FileName: string; Owner: TComponent;
  139.   const Name: string): TComponent;
  140. // Create and read a component from the TXmlNode ANode. In order to successfully
  141. // create the component from scratch, the component's class must be registered
  142. // beforehand with a call to RegisterClass. Specify Owner to add the component
  143. // as a child to Owner's component list. This is usually a form. Specify Name
  144. // as the new component name for the created component.
  145. function ComponentCreateFromXmlNode(ANode: TXmlNode; Owner: TComponent;
  146.   const Name: string): TComponent;
  147. // Create and read a component from the XML stream S. In order to successfully
  148. // create the component from scratch, the component's class must be registered
  149. // beforehand with a call to RegisterClass. Specify Owner to add the component
  150. // as a child to Owner's component list. This is usually a form. Specify Name
  151. // as the new component name for the created component.
  152. function ComponentCreateFromXmlStream(S: TStream; Owner: TComponent;
  153.   const Name: string): TComponent;
  154. // Create and read a component from the XML in string in Value. In order to successfully
  155. // create the component from scratch, the component's class must be registered
  156. // beforehand with a call to RegisterClass. Specify Owner to add the component
  157. // as a child to Owner's component list. This is usually a form. Specify Name
  158. // as the new component name for the created component.
  159. function ComponentCreateFromXmlString(const Value: string; Owner: TComponent;
  160.   const Name: string): TComponent;
  161. // Create and read a form from the XML file with FileName. In order to successfully
  162. // create the form from scratch, the form's class must be registered
  163. // beforehand with a call to RegisterClass. Specify Owner to add the form
  164. // as a child to Owner's component list. For forms this is usually Application.
  165. // Specify Name as the new form name for the created form.
  166. function FormCreateFromXmlFile(const FileName: string; Owner: TComponent;
  167.   const Name: string): TForm;
  168. // Create and read a form from the XML stream in S. In order to successfully
  169. // create the form from scratch, the form's class must be registered
  170. // beforehand with a call to RegisterClass. Specify Owner to add the form
  171. // as a child to Owner's component list. For forms this is usually Application.
  172. // Specify Name as the new form name for the created form.
  173. function FormCreateFromXmlStream(S: TStream; Owner: TComponent;
  174.   const Name: string): TForm;
  175. // Create and read a form from the XML string in Value. In order to successfully
  176. // create the form from scratch, the form's class must be registered
  177. // beforehand with a call to RegisterClass. Specify Owner to add the form
  178. // as a child to Owner's component list. For forms this is usually Application.
  179. // Specify Name as the new form name for the created form.
  180. function FormCreateFromXmlString(const Value: string; Owner: TComponent;
  181.   const Name: string): TForm;
  182. // High-level load methods
  183. // Load all the published properties of AObject from the XML file in Filename.
  184. // Specify AParent in order to resolve references to parent methods and
  185. // events correctly.
  186. procedure ObjectLoadFromXmlFile(AObject: TObject; const FileName: string;
  187.   AParent: TComponent = nil);
  188. // Load all the published properties of AObject from the TXmlNode ANode.
  189. // Specify AParent in order to resolve references to parent methods and
  190. // events correctly.
  191. procedure ObjectLoadFromXmlNode(AObject: TObject; ANode: TXmlNode; AParent: TComponent = nil);
  192. // Load all the published properties of AObject from the XML stream in S.
  193. // Specify AParent in order to resolve references to parent methods and
  194. // events correctly.
  195. procedure ObjectLoadFromXmlStream(AObject: TObject; S: TStream; AParent: TComponent = nil);
  196. // Load all the published properties of AObject from the XML string in Value.
  197. // Specify AParent in order to resolve references to parent methods and
  198. // events correctly.
  199. procedure ObjectLoadFromXmlString(AObject: TObject; const Value: string; AParent: TComponent = nil);
  200. // High-level save methods
  201. // Save all the published properties of AObject as XML to the file in Filename.
  202. // Specify AParent in order to store references to parent methods and
  203. // events correctly.
  204. procedure ObjectSaveToXmlFile(AObject: TObject; const FileName: string;
  205.   AParent: TComponent = nil);
  206. // Save all the published properties of AObject to the TXmlNode ANode.
  207. // Specify AParent in order to store references to parent methods and
  208. // events correctly.
  209. procedure ObjectSaveToXmlNode(AObject: TObject; ANode: TXmlNode; AParent: TComponent = nil);
  210. // Save all the published properties of AObject as XML in stream S.
  211. // Specify AParent in order to store references to parent methods and
  212. // events correctly.
  213. procedure ObjectSaveToXmlStream(AObject: TObject; S: TStream; AParent: TComponent = nil);
  214. // Save all the published properties of AObject as XML in string Value.
  215. // Specify AParent in order to store references to parent methods and
  216. // events correctly.
  217. function ObjectSaveToXmlString(AObject: TObject; AParent: TComponent = nil): string;
  218. // Save all the published properties of AComponent as XML in the file in Filename.
  219. // Specify AParent in order to store references to parent methods and
  220. // events correctly.
  221. procedure ComponentSaveToXmlFile(AComponent: TComponent; const FileName: string;
  222.   AParent: TComponent = nil);
  223. // Save all the published properties of AComponent to the TXmlNode ANode.
  224. // Specify AParent in order to store references to parent methods and
  225. // events correctly.
  226. procedure ComponentSaveToXmlNode(AComponent: TComponent; ANode: TXmlNode;
  227.   AParent: TComponent = nil);
  228. // Save all the published properties of AComponent as XML in the stream in S.
  229. // Specify AParent in order to store references to parent methods and
  230. // events correctly.
  231. procedure ComponentSaveToXmlStream(AComponent: TComponent; S: TStream;
  232.   AParent: TComponent = nil);
  233. // Save all the published properties of AComponent as XML in the string Value.
  234. // Specify AParent in order to store references to parent methods and
  235. // events correctly.
  236. function ComponentSaveToXmlString(AComponent: TComponent; AParent: TComponent = nil): string;
  237. // Save the form AForm as XML to the file in Filename. This method also stores
  238. // properties of all child components on the form, and can therefore be used
  239. // as a form-storage method.
  240. procedure FormSaveToXmlFile(AForm: TForm; const FileName: string);
  241. // Save the form AForm as XML to the stream in S. This method also stores
  242. // properties of all child components on the form, and can therefore be used
  243. // as a form-storage method.
  244. procedure FormSaveToXmlStream(AForm: TForm; S: TStream);
  245. // Save the form AForm as XML to a string. This method also stores
  246. // properties of all child components on the form, and can therefore be used
  247. // as a form-storage method.
  248. function FormSaveToXmlString(AForm: TForm): string;
  249. resourcestring
  250.   sxwIllegalVarType        = 'Illegal variant type';
  251.   sxrUnregisteredClassType = 'Unregistered classtype encountered';
  252.   sxrInvalidPropertyValue  = 'Invalid property value';
  253.   sxwInvalidMethodName     = 'Invalid method name';
  254. implementation
  255. {$IFDEF TRIALXML}
  256. uses
  257.   Dialogs;
  258. {$ENDIF}
  259. type
  260.   THackPersistent = class(TPersistent);
  261.   THackComponent = class(TComponent)
  262.   public
  263.     procedure SetComponentState(const AState: TComponentState);
  264.   published
  265.     property ComponentState;
  266.   end;
  267.   THackReader = class(TReader);
  268. function ComponentCreateFromXmlFile(const FileName: string; Owner: TComponent;
  269.   const Name: string): TComponent;
  270. var
  271.   S: TStream;
  272. begin
  273.   S := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
  274.   try
  275.     Result := ComponentCreateFromXmlStream(S, Owner, Name);
  276.   finally
  277.     S.Free;
  278.   end;
  279. end;
  280. function ComponentCreateFromXmlNode(ANode: TXmlNode; Owner: TComponent;
  281.   const Name: string): TComponent;
  282. var
  283.   AReader: TsdXmlObjectReader;
  284. begin
  285.   Result := nil;
  286.   if not assigned(ANode) then exit;
  287.   // Create reader
  288.   AReader := TsdXmlObjectReader.Create;
  289.   try
  290.     // Read the component from the node
  291.     Result := AReader.CreateComponent(ANode, Owner, nil, Name);
  292.   finally
  293.     AReader.Free;
  294.   end;
  295. end;
  296. function ComponentCreateFromXmlStream(S: TStream; Owner: TComponent;
  297.   const Name: string): TComponent;
  298. var
  299.   ADoc: TNativeXml;
  300. begin
  301.   Result := nil;
  302.   if not assigned(S) then exit;
  303.   // Create XML document
  304.   ADoc := TNativeXml.Create;
  305.   try
  306.     // Load XML
  307.     ADoc.LoadFromStream(S);
  308.     // Load from XML node
  309.     Result := ComponentCreateFromXmlNode(ADoc.Root, Owner, Name);
  310.   finally
  311.     ADoc.Free;
  312.   end;
  313. end;
  314. function ComponentCreateFromXmlString(const Value: string; Owner: TComponent;
  315.   const Name: string): TComponent;
  316. var
  317.   S: TStream;
  318. begin
  319.   S := TStringStream.Create(Value);
  320.   try
  321.     Result := ComponentCreateFromXmlStream(S, Owner, Name);
  322.   finally
  323.     S.Free;
  324.   end;
  325. end;
  326. function FormCreateFromXmlFile(const FileName: string; Owner: TComponent;
  327.   const Name: string): TForm;
  328. var
  329.   S: TStream;
  330. begin
  331.   S := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
  332.   try
  333.     Result := FormCreateFromXmlStream(S, Owner, Name);
  334.   finally
  335.     S.Free;
  336.   end;
  337. end;
  338. function FormCreateFromXmlStream(S: TStream; Owner: TComponent;
  339.   const Name: string): TForm;
  340. var
  341.   ADoc: TNativeXml;
  342. begin
  343.   Result := nil;
  344.   if not assigned(S) then exit;
  345.   // Create XML document
  346.   ADoc := TNativeXml.Create;
  347.   try
  348.     // Load XML
  349.     ADoc.LoadFromStream(S);
  350.     // Load from XML node
  351.     Result := TForm(ComponentCreateFromXmlNode(ADoc.Root, Owner, Name));
  352.   finally
  353.     ADoc.Free;
  354.   end;
  355. end;
  356. function FormCreateFromXmlString(const Value: string; Owner: TComponent;
  357.   const Name: string): TForm;
  358. var
  359.   S: TStream;
  360. begin
  361.   S := TStringStream.Create(Value);
  362.   try
  363.     Result := FormCreateFromXmlStream(S, Owner, Name);
  364.   finally
  365.     S.Free;
  366.   end;
  367. end;
  368. procedure ObjectLoadFromXmlFile(AObject: TObject; const FileName: string;
  369.   AParent: TComponent = nil);
  370. var
  371.   S: TStream;
  372. begin
  373.   S := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
  374.   try
  375.     ObjectLoadFromXmlStream(AObject, S, AParent);
  376.   finally
  377.     S.Free;
  378.   end;
  379. end;
  380. procedure ObjectLoadFromXmlNode(AObject: TObject; ANode: TXmlNode; AParent: TComponent = nil);
  381. var
  382.   AReader: TsdXmlObjectReader;
  383. begin
  384.   if not assigned(AObject) or not assigned(ANode) then exit;
  385.   // Create writer
  386.   AReader := TsdXmlObjectReader.Create;
  387.   try
  388.     // Write the object to the document
  389.     if AObject is TComponent then
  390.       AReader.ReadComponent(ANode, TComponent(AObject), AParent)
  391.     else
  392.       AReader.ReadObject(ANode, AObject, AParent);
  393.   finally
  394.     AReader.Free;
  395.   end;
  396. end;
  397. procedure ObjectLoadFromXmlStream(AObject: TObject; S: TStream; AParent: TComponent = nil);
  398. var
  399.   ADoc: TNativeXml;
  400. begin
  401.   if not assigned(S) then exit;
  402.   // Create XML document
  403.   ADoc := TNativeXml.Create;
  404.   try
  405.     // Load XML
  406.     ADoc.LoadFromStream(S);
  407.     // Load from XML node
  408.     ObjectLoadFromXmlNode(AObject, ADoc.Root, AParent);
  409.   finally
  410.     ADoc.Free;
  411.   end;
  412. end;
  413. procedure ObjectLoadFromXmlString(AObject: TObject; const Value: string; AParent: TComponent = nil);
  414. var
  415.   S: TStringStream;
  416. begin
  417.   S := TStringStream.Create(Value);
  418.   try
  419.     ObjectLoadFromXmlStream(AObject, S, AParent);
  420.   finally
  421.     S.Free;
  422.   end;
  423. end;
  424. procedure ObjectSaveToXmlFile(AObject: TObject; const FileName: string;
  425.   AParent: TComponent = nil);
  426. var
  427.   S: TStream;
  428. begin
  429.   S := TFileStream.Create(FileName, fmCreate);
  430.   try
  431.     ObjectSaveToXmlStream(AObject, S, AParent);
  432.   finally
  433.     S.Free;
  434.   end;
  435. end;
  436. procedure ObjectSaveToXmlNode(AObject: TObject; ANode: TXmlNode; AParent: TComponent = nil);
  437. var
  438.   AWriter: TsdXmlObjectWriter;
  439. begin
  440.   if not assigned(AObject) or not assigned(ANode) then exit;
  441.   // Create writer
  442.   AWriter := TsdXmlObjectWriter.Create;
  443.   try
  444.     // Write the object to the document
  445.     if AObject is TComponent then
  446.       AWriter.WriteComponent(ANode, TComponent(AObject), AParent)
  447.     else begin
  448.       ANode.Name := AObject.ClassName;
  449.       AWriter.WriteObject(ANode, AObject, AParent);
  450.     end;
  451.   finally
  452.     AWriter.Free;
  453.   end;
  454. end;
  455. procedure ObjectSaveToXmlStream(AObject: TObject; S: TStream; AParent: TComponent = nil);
  456. var
  457.   ADoc: TNativeXml;
  458. begin
  459.   if not assigned(S) then exit;
  460.   // Create XML document
  461.   ADoc := TNativeXml.Create;
  462.   try
  463.     ADoc.Utf8Encoded := True;
  464.     ADoc.EncodingString := 'UTF-8';
  465.     ADoc.ExternalEncoding := seUTF8;
  466.     ADoc.XmlFormat := xfReadable;
  467.     // Save to XML node
  468.     ObjectSaveToXmlNode(AObject, ADoc.Root, AParent);
  469.     // Save to stream
  470.     ADoc.SaveToStream(S);
  471.   finally
  472.     ADoc.Free;
  473.   end;
  474. end;
  475. function ObjectSaveToXmlString(AObject: TObject; AParent: TComponent = nil): string;
  476. var
  477.   S: TStringStream;
  478. begin
  479.   S := TStringStream.Create('');
  480.   try
  481.     ObjectSaveToXmlStream(AObject, S, AParent);
  482.     Result := S.DataString;
  483.   finally
  484.     S.Free;
  485.   end;
  486. end;
  487. procedure ComponentSaveToXmlFile(AComponent: TComponent; const FileName: string;
  488.   AParent: TComponent = nil);
  489. begin
  490.   ObjectSaveToXmlFile(AComponent, FileName, AParent);
  491. end;
  492. procedure ComponentSaveToXmlNode(AComponent: TComponent; ANode: TXmlNode;
  493.   AParent: TComponent = nil);
  494. begin
  495.   ObjectSaveToXmlNode(AComponent, ANode, AParent);
  496. end;
  497. procedure ComponentSaveToXmlStream(AComponent: TComponent; S: TStream;
  498.   AParent: TComponent = nil);
  499. begin
  500.   ObjectSaveToXmlStream(AComponent, S, AParent);
  501. end;
  502. function ComponentSaveToXmlString(AComponent: TComponent; AParent: TComponent = nil): string;
  503. begin
  504.   Result := ObjectSaveToXmlString(AComponent, AParent);
  505. end;
  506. procedure FormSaveToXmlFile(AForm: TForm; const FileName: string);
  507. begin
  508.   ComponentSaveToXmlFile(AForm, FileName, AForm);
  509. end;
  510. procedure FormSaveToXmlStream(AForm: TForm; S: TStream);
  511. begin
  512.   ComponentSaveToXmlStream(AForm, S, AForm);
  513. end;
  514. function FormSaveToXmlString(AForm: TForm): string;
  515. begin
  516.   Result := ComponentSaveToXmlString(AForm, AForm);
  517. end;
  518. { TsdXmlObjectWriter }
  519. procedure TsdXmlObjectWriter.WriteComponent(ANode: TXmlNode; AComponent,
  520.   AParent: TComponent);
  521. begin
  522.   if not assigned(ANode) or not assigned(AComponent) then exit;
  523.   ANode.Name := AComponent.ClassName;
  524.   if length(AComponent.Name) > 0 then
  525.     ANode.AttributeAdd('Name', AComponent.Name);
  526.   WriteObject(ANode, AComponent, AParent);
  527. end;
  528. procedure TsdXmlObjectWriter.WriteObject(ANode: TXmlNode; AObject: TObject;
  529.   AParent: TComponent);
  530. var
  531.   i, Count: Integer;
  532.   PropInfo: PPropInfo;
  533.   PropList: PPropList;
  534.   S: TStringStream;
  535.   AWriter: TWriter;
  536.   AChildNode: TXmlNode;
  537.   AComponentNode: TXmlNode;
  538. begin
  539.   if not assigned(ANode) or not assigned(AObject) then exit;
  540.   // If this is a component, store child components
  541.   if AObject is TComponent then with TComponent(AObject) do begin
  542.     if ComponentCount > 0 then begin
  543.       AChildNode := ANode.NodeNew('Components');
  544.       for i := 0 to ComponentCount - 1 do begin
  545.         AComponentNode := AChildNode.NodeNew(Components[i].ClassName);
  546.         if length(Components[i].Name) > 0 then
  547.           AComponentNode.AttributeAdd('Name', Components[i].Name);
  548.         WriteObject(AComponentNode, Components[i], TComponent(AObject));
  549.       end;
  550.     end;
  551.   end;
  552.   // Save all regular properties that need storing
  553.   Count := GetTypeData(AObject.ClassInfo)^.PropCount;
  554.   if Count > 0 then begin
  555.     GetMem(PropList, Count * SizeOf(Pointer));
  556.     try
  557.       GetPropInfos(AObject.ClassInfo, PropList);
  558.       for i := 0 to Count - 1 do begin
  559.         PropInfo := PropList^[i];
  560.         if PropInfo = nil then continue;
  561.         if IsStoredProp(AObject, PropInfo) then
  562.           WriteProperty(ANode, AObject, AParent, PropInfo);
  563.       end;
  564.     finally
  565.       FreeMem(PropList, Count * SizeOf(Pointer));
  566.     end;
  567.   end;
  568.   // Save defined properties
  569.   if AObject is TPersistent then begin
  570.     S := TStringStream.Create('');
  571.     try
  572.       AWriter := TWriter.Create(S, 4096);
  573.       try
  574.         THackPersistent(AObject).DefineProperties(AWriter);
  575.       finally
  576.         AWriter.Free;
  577.       end;
  578.       // Do we have data from DefineProperties?
  579.       if S.Size > 0 then begin
  580.         // Yes, add a node with binary data
  581.         ANode.NodeNew('DefinedProperties').BinaryString := S.DataString;
  582.       end;
  583.     finally
  584.       S.Free;
  585.     end;
  586.   end;
  587. end;
  588. procedure TsdXmlObjectWriter.WriteProperty(ANode: TXmlNode; AObject: TObject;
  589.   AParent: TComponent; PropInfo: PPropInfo);
  590. var
  591.   PropType: PTypeInfo;
  592.   AChildNode: TXmlNode;
  593.   ACollectionNode: TXmlNode;
  594.   procedure WritePropName;
  595.   begin
  596.     AChildNode := ANode.NodeNew(PPropInfo(PropInfo)^.Name);
  597.   end;
  598.   procedure WriteInteger(Value: Int64);
  599.   begin
  600.     AChildNode.ValueAsString := IntToStr(Value);
  601.   end;
  602.   procedure WriteString(Value: string);
  603.   begin
  604.     AChildNode.ValueAsString := Value;
  605.   end;
  606.   procedure WriteSet(Value: Longint);
  607.   var
  608.     I: Integer;
  609.     BaseType: PTypeInfo;
  610.     S, Enum: string;
  611.   begin
  612.     BaseType := GetTypeData(PropType)^.CompType^;
  613.     for i := 0 to SizeOf(TIntegerSet) * 8 - 1 do begin
  614.       if i in TIntegerSet(Value) then begin
  615.         Enum := GetEnumName(BaseType, i);
  616.         if i > 0 then
  617.           S := S + ',' + Enum
  618.         else
  619.           S := Enum;
  620.       end;
  621.     end;
  622.     AChildNode.ValueAsString := Format('[%s]', [S]);
  623.   end;
  624.   procedure WriteIntProp(IntType: PTypeInfo; Value: Longint);
  625.   var
  626.     Ident: string;
  627.     IntToIdent: TIntToIdent;
  628.   begin
  629.     IntToIdent := FindIntToIdent(IntType);
  630.     if Assigned(IntToIdent) and IntToIdent(Value, Ident) then
  631.       WriteString(Ident)
  632.     else
  633.       WriteInteger(Value);
  634.   end;
  635.   procedure WriteCollectionProp(Collection: TCollection);
  636.   var
  637.     i: integer;
  638.   begin
  639.     if assigned(Collection) then begin
  640.       for i := 0 to Collection.Count - 1 do
  641.       begin
  642.         ACollectionNode := AChildNode.NodeNew(Collection.Items[i].ClassName);
  643.         WriteObject(ACollectionNode, Collection.Items[I], AParent);
  644.       end;
  645.     end;
  646.   end;
  647.   procedure WriteOrdProp;
  648.   var
  649.     Value: Longint;
  650.   begin
  651.     Value := GetOrdProp(AObject, PropInfo);
  652.     if not (Value = PPropInfo(PropInfo)^.Default) then begin
  653.       WritePropName;
  654.       case PropType^.Kind of
  655.       tkInteger:     WriteIntProp(PPropInfo(PropInfo)^.PropType^, Value);
  656.       tkChar:        WriteString(Chr(Value));
  657.       tkSet:         WriteSet(Value);
  658.       tkEnumeration: WriteString(GetEnumName(PropType, Value));
  659.       end;
  660.     end;
  661.   end;
  662.   procedure WriteFloatProp;
  663.   var
  664.     Value: Extended;
  665.   begin
  666.     Value := GetFloatProp(AObject, PropInfo);
  667.     if not (Value = 0) then
  668.       ANode.WriteFloat(PPropInfo(PropInfo)^.Name, Value);
  669.   end;
  670.   procedure WriteInt64Prop;
  671.   var
  672.     Value: Int64;
  673.   begin
  674.     Value := GetInt64Prop(AObject, PropInfo);
  675.     if not (Value = 0) then
  676.       ANode.WriteInt64(PPropInfo(PropInfo)^.Name, Value);
  677.   end;
  678.   procedure WriteStrProp;
  679.   var
  680.     Value: string;
  681.   begin
  682.     Value := GetStrProp(AObject, PropInfo);
  683.     if not (length(Value) = 0) then
  684.       ANode.WriteString(PPropInfo(PropInfo)^.Name, Value);
  685.   end;
  686.   {$IFDEF D6UP}
  687.   procedure WriteWideStrProp;
  688.   var
  689.     Value: WideString;
  690.   begin
  691.     Value := GetWideStrProp(AObject, PropInfo);
  692.     if not (length(Value) = 0) then
  693.       ANode.WriteWidestring(PPropInfo(PropInfo)^.Name, Value);
  694.   end;
  695.   {$ENDIF}
  696.   procedure WriteObjectProp;
  697.   var
  698.     Value: TObject;
  699.     ComponentName: string;
  700.     function GetComponentName(Component: TComponent): string;
  701.     begin
  702.       if Component.Owner = AParent then
  703.         Result := Component.Name
  704.       else if Component = AParent then
  705.         Result := 'Owner'
  706.       else if assigned(Component.Owner) and (length(Component.Owner.Name) > 0)
  707.         and (length(Component.Name) > 0) then
  708.         Result := Component.Owner.Name + '.' + Component.Name
  709.       else if length(Component.Name) > 0 then
  710.         Result := Component.Name + '.Owner'
  711.       else Result := '';
  712.     end;
  713.   begin
  714.     Value := TObject(GetOrdProp(AObject, PropInfo));
  715.     if not assigned(Value) then exit;
  716.     WritePropName;
  717.     if Value is TComponent then begin
  718.       ComponentName := GetComponentName(TComponent(Value));
  719.       if length(ComponentName) > 0 then
  720.         WriteString(ComponentName);
  721.     end else begin
  722.       WriteString(Format('(%s)', [Value.ClassName]));
  723.       if Value is TCollection then
  724.         WriteCollectionProp(TCollection(Value))
  725.       else begin
  726.         if AObject is TComponent then
  727.           WriteObject(AChildNode, Value, TComponent(AObject))
  728.         else
  729.           WriteObject(AChildNode, Value, AParent)
  730.       end;
  731.       // No need to store an empty child.. so check and remove
  732.       if AChildNode.NodeCount = 0 then
  733.         ANode.NodeRemove(AChildNode);
  734.     end;
  735.   end;
  736.   procedure WriteMethodProp;
  737.   var
  738.     Value: TMethod;
  739.     function IsDefaultValue: Boolean;
  740.     begin
  741.       Result := (Value.Code = nil) or
  742.         ((Value.Code <> nil) and assigned(AParent) and (AParent.MethodName(Value.Code) = ''));
  743.     end;
  744.   begin
  745.     Value := GetMethodProp(AObject, PropInfo);
  746.     if not IsDefaultValue then begin
  747.       if assigned(Value.Code) then begin
  748.         WritePropName;
  749.         if assigned(AParent) then
  750.           WriteString(AParent.MethodName(Value.Code))
  751.         else
  752.           AChildNode.ValueAsString := '???';
  753.       end;
  754.     end;
  755.   end;
  756.   procedure WriteVariantProp;
  757.   var
  758.     AValue: Variant;
  759.     ACurrency: Currency;
  760.   var
  761.     VType: Integer;
  762.   begin
  763.     AValue := GetVariantProp(AObject, PropInfo);
  764.     if not VarIsEmpty(AValue) then begin
  765.       if VarIsArray(AValue) then
  766.         raise Exception.Create(sxwIllegalVarType);
  767.       WritePropName;
  768.       VType := VarType(AValue);
  769.       AChildNode.AttributeAdd('VarType', IntToHex(VType, 4));
  770.       case VType and varTypeMask of
  771.       varOleStr:  AChildNode.ValueAsWideString := AValue;
  772.       varString:  AChildNode.ValueAsString := AValue;
  773.       varByte,
  774.       varSmallInt,
  775.       varInteger: AChildNode.ValueAsInteger := AValue;
  776.       varSingle,
  777.       varDouble:  AChildNode.ValueAsFloat := AValue;
  778.       varCurrency:
  779.         begin
  780.           ACurrency := AValue;
  781.           AChildNode.BufferWrite(ACurrency, SizeOf(ACurrency));
  782.         end;
  783.       varDate:    AChildNode.ValueAsDateTime := AValue;
  784.       varBoolean: AChildNode.ValueAsBool := AValue;
  785.       else
  786.         try
  787.           ANode.ValueAsString := AValue;
  788.         except
  789.           raise Exception.Create(sxwIllegalVarType);
  790.         end;
  791.       end;//case
  792.     end;
  793.   end;
  794. begin
  795.   if (PPropInfo(PropInfo)^.SetProc <> nil) and
  796.     (PPropInfo(PropInfo)^.GetProc <> nil) then
  797.   begin
  798.     PropType := PPropInfo(PropInfo)^.PropType^;
  799.     case PropType^.Kind of
  800.     tkInteger, tkChar, tkEnumeration, tkSet: WriteOrdProp;
  801.     tkFloat:                                 WriteFloatProp;
  802.     tkString, tkLString:                     WriteStrProp;
  803.     {$IFDEF D6UP}
  804.     tkWString:                               WriteWideStrProp;
  805.     {$ENDIF}
  806.     tkClass:                                 WriteObjectProp;
  807.     tkMethod:                                WriteMethodProp;
  808.     tkVariant:                               WriteVariantProp;
  809.     tkInt64:                                 WriteInt64Prop;
  810.     end;
  811.   end;
  812. end;
  813. { TsdXmlObjectReader }
  814. function TsdXmlObjectReader.CreateComponent(ANode: TXmlNode;
  815.   AOwner, AParent: TComponent; AName: string): TComponent;
  816. var
  817.   AClass: TComponentClass;
  818. begin
  819.   AClass := TComponentClass(GetClass(ANode.Name));
  820.   if not assigned(AClass) then
  821.     raise Exception.Create(sxrUnregisteredClassType);
  822.   Result := AClass.Create(AOwner);
  823.   if length(AName) = 0 then
  824.     Result.Name := ANode.AttributeByName['Name']
  825.   else
  826.     Result.Name := AName;
  827.   if not assigned(AParent) then
  828.     AParent := Result;
  829.   ReadComponent(ANode, Result, AParent);
  830. end;
  831. procedure TsdXmlObjectReader.ReadComponent(ANode: TXmlNode; AComponent,
  832.   AParent: TComponent);
  833. begin
  834.   ReadObject(ANode, AComponent, AParent);
  835. end;
  836. procedure TsdXmlObjectReader.ReadObject(ANode: TXmlNode; AObject: TObject; AParent: TComponent);
  837. var
  838.   i, Count: Integer;
  839.   PropInfo: PPropInfo;
  840.   PropList: PPropList;
  841.   S: TStringStream;
  842.   AReader: TReader;
  843.   AChildNode: TXmlNode;
  844.   AComponentNode: TXmlNode;
  845.   AClass: TComponentClass;
  846.   AComponent: TComponent;
  847. begin
  848.   if not assigned(ANode) or not assigned(AObject) then exit;
  849.   // Start loading
  850.   if AObject is TComponent then with THackComponent(AObject) do begin
  851.     THackComponent(AObject).Updating;
  852.     SetComponentState(ComponentState + [csLoading, csReading]);
  853.   end;
  854.   try
  855.     // If this is a component, load child components
  856.     if AObject is TComponent then with TComponent(AObject) do begin
  857.       AChildNode := ANode.NodeByName('Components');
  858.       if assigned(AChildNode) then begin
  859.         for i := 0 to AChildNode.NodeCount - 1 do begin
  860.           AComponentNode := AChildNode.Nodes[i];
  861.           AComponent := FindComponent(AComponentNode.AttributeByName['Name']);
  862.           if not assigned(AComponent) then begin
  863.             AClass := TComponentClass(GetClass(AComponentNode.Name));
  864.             if not assigned(AClass) then
  865.               raise Exception.Create(sxrUnregisteredClassType);
  866.             AComponent := AClass.Create(TComponent(AObject));
  867.             AComponent.Name := AComponentNode.AttributeByName['Name'];
  868.             // In case of new (visual) controls we set the parent
  869.             if (AComponent is TControl) and (AObject is TWinControl) then
  870.               TControl(AComponent).Parent := TWinControl(AObject);
  871.           end;
  872.           ReadComponent(AComponentNode, AComponent, TComponent(AObject));
  873.         end;
  874.       end;
  875.     end;
  876.     // Load all loadable regular properties
  877.     Count := GetTypeData(AObject.ClassInfo)^.PropCount;
  878.     if Count > 0 then begin
  879.       GetMem(PropList, Count * SizeOf(Pointer));
  880.       try
  881.         GetPropInfos(AObject.ClassInfo, PropList);
  882.         for i := 0 to Count - 1 do begin
  883.           PropInfo := PropList^[i];
  884.           if PropInfo = nil then continue;
  885.           if IsStoredProp(AObject, PropInfo) then
  886.             ReadProperty(ANode, AObject, AParent, PropInfo);
  887.         end;
  888.       finally
  889.         FreeMem(PropList, Count * SizeOf(Pointer));
  890.       end;
  891.     end;
  892.     // Load defined properties
  893.     if AObject is TPersistent then begin
  894.       AChildNode := ANode.NodeByName('DefinedProperties');
  895.       if assigned(AChildNode) then begin
  896.         S := TStringStream.Create(AChildNode.BinaryString);
  897.         try
  898.           AReader := TReader.Create(S, 4096);
  899.           try
  900.             THackReader(AReader).ReadProperty(TPersistent(AObject));
  901.           finally
  902.             AReader.Free;
  903.           end;
  904.         finally
  905.           S.Free;
  906.         end;
  907.       end;
  908.     end;
  909.   finally
  910.     // End loading
  911.     if AObject is TComponent then with THackComponent(AObject) do begin
  912.       SetComponentState(ComponentState - [csReading]);
  913.       THackComponent(AObject).Loaded;
  914.       THackComponent(AObject).Updated;
  915.     end;
  916.   end;
  917. end;
  918. procedure TsdXmlObjectReader.ReadProperty(ANode: TXmlNode;
  919.   AObject: TObject; AParent: TComponent; PropInfo: PPropInfo);
  920. var
  921.   PropType: PTypeInfo;
  922.   AChildNode: TXmlNode;
  923.   Method: TMethod;
  924.   PropObject: TObject;
  925.   procedure SetSetProp(const AValue: string);
  926.   var
  927.     S: string;
  928.     P: integer;
  929.     ASet: integer;
  930.     EnumType: PTypeInfo;
  931.     procedure AddToEnum(const EnumName: string);
  932.     var
  933.       V: integer;
  934.     begin
  935.       if length(EnumName) = 0 then exit;
  936.       V := GetEnumValue(EnumType, EnumName);
  937.       if V = -1 then
  938.         raise Exception.Create(sxrInvalidPropertyValue);
  939.       Include(TIntegerSet(ASet), V);
  940.     end;
  941.   begin
  942.     ASet := 0;
  943.     EnumType := GetTypeData(PropType)^.CompType^;
  944.     S := copy(AValue, 2, length(AValue) - 2);
  945.     repeat
  946.       P := Pos(',', S);
  947.       if P > 0 then begin
  948.         AddToEnum(copy(S, 1, P - 1));
  949.         S := copy(S, P + 1, length(S));
  950.       end else begin
  951.         AddToEnum(S);
  952.         break;
  953.       end;
  954.     until False;
  955.     SetOrdProp(AObject, PropInfo, ASet);
  956.   end;
  957.   procedure SetIntProp(const AValue: string);
  958.   var
  959.     V: Longint;
  960.     IdentToInt: TIdentToInt;
  961.   begin
  962.     IdentToInt := FindIdentToInt(PropType);
  963.     if Assigned(IdentToInt) and IdentToInt(AValue, V) then
  964.       SetOrdProp(AObject, PropInfo, V)
  965.     else
  966.       SetOrdProp(AObject, PropInfo, StrToInt(AValue));
  967.   end;
  968.   procedure SetCharProp(const AValue: string);
  969.   begin
  970.     if length(AValue) <> 1 then
  971.       raise Exception.Create(sxrInvalidPropertyValue);
  972.     SetOrdProp(AObject, PropInfo, Ord(AValue[1]));
  973.   end;
  974.   procedure SetEnumProp(const AValue: string);
  975.   var
  976.     V: integer;
  977.   begin
  978.     V := GetEnumValue(PropType, AValue);
  979.     if V = -1 then
  980.       raise Exception.Create(sxrInvalidPropertyValue);
  981.     SetOrdProp(AObject, PropInfo, V)
  982.   end;
  983.   procedure ReadCollectionProp(ACollection: TCollection);
  984.   var
  985.     i: integer;
  986.     Item: TPersistent;
  987.   begin
  988.     ACollection.BeginUpdate;
  989.     try
  990.       ACollection.Clear;
  991.       for i := 0 to AChildNode.NodeCount - 1 do begin
  992.         Item := ACollection.Add;
  993.         ReadObject(AChildNode.Nodes[i], Item, AParent);
  994.       end;
  995.     finally
  996.       ACollection.EndUpdate;
  997.     end;
  998.   end;
  999.   procedure SetObjectProp(const AValue: string);
  1000.   var
  1001.     AClassName: string;
  1002.     PropObject: TObject;
  1003.     Reference: TComponent;
  1004.   begin
  1005.     if length(AValue) = 0 then exit;
  1006.     if AValue[1] = '(' then begin
  1007.       // Persistent class
  1008.       AClassName := Copy(AValue, 2, length(AValue) - 2);
  1009.       PropObject := TObject(GetOrdProp(AObject, PropInfo));
  1010.       if assigned(PropObject) and (PropObject.ClassName = AClassName) then begin
  1011.         if PropObject is TCollection then
  1012.           ReadCollectionProp(TCollection(PropObject))
  1013.         else begin
  1014.           if AObject is TComponent then
  1015.             ReadObject(AChildNode, PropObject, TComponent(AObject))
  1016.           else
  1017.             ReadObject(AChildNode, PropObject, AParent);
  1018.         end;
  1019.       end else
  1020.         raise Exception.Create(sxrUnregisteredClassType);
  1021.     end else begin
  1022.       // Component reference
  1023.       if assigned(AParent) then begin
  1024.         Reference := FindNestedComponent(AParent, AValue);
  1025.         SetOrdProp(AObject, PropInfo, Longint(Reference));
  1026.       end;
  1027.     end;
  1028.   end;
  1029.   procedure SetMethodProp(const AValue: string);
  1030.   var
  1031.     Method: TMethod;
  1032.   begin
  1033.     // to do: add OnFindMethod
  1034.     if not assigned(AParent) then exit;
  1035.     Method.Code := AParent.MethodAddress(AValue);
  1036.     if not assigned(Method.Code) then
  1037.       raise Exception.Create(sxwInvalidMethodName);
  1038.     Method.Data := AParent;
  1039.     TypInfo.SetMethodProp(AObject, PropInfo, Method);
  1040.   end;
  1041.   procedure SetVariantProp(const AValue: string);
  1042.   var
  1043.     VType: integer;
  1044.     Value: Variant;
  1045.     ACurrency: Currency;
  1046.   begin
  1047.     VType := StrToInt(AChildNode.AttributeByName['VarType']);
  1048.     case VType and varTypeMask of
  1049.     varOleStr:  Value := AChildNode.ValueAsWideString;
  1050.     varString:  Value := AChildNode.ValueAsString;
  1051.     varByte,
  1052.     varSmallInt,
  1053.     varInteger: Value := AChildNode.ValueAsInteger;
  1054.     varSingle,
  1055.     varDouble:  Value := AChildNode.ValueAsFloat;
  1056.     varCurrency:
  1057.       begin
  1058.         AChildNode.BufferWrite(ACurrency, SizeOf(ACurrency));
  1059.         Value := ACurrency;
  1060.       end;
  1061.     varDate:    Value := AChildNode.ValueAsDateTime;
  1062.     varBoolean: Value := AChildNode.ValueAsBool;
  1063.     else
  1064.       try
  1065.         Value := ANode.ValueAsString;
  1066.       except
  1067.         raise Exception.Create(sxwIllegalVarType);
  1068.       end;
  1069.     end;//case
  1070.     TVarData(Value).VType := VType;
  1071.     TypInfo.SetVariantProp(AObject, PropInfo, Value);
  1072.   end;
  1073. begin
  1074.   if (PPropInfo(PropInfo)^.SetProc <> nil) and
  1075.     (PPropInfo(PropInfo)^.GetProc <> nil) then
  1076.   begin
  1077.     PropType := PPropInfo(PropInfo)^.PropType^;
  1078.     AChildNode := ANode.NodeByName(PPropInfo(PropInfo)^.Name);
  1079.     if assigned(AChildNode) then begin
  1080.       // Non-default values from XML
  1081.       case PropType^.Kind of
  1082.       tkInteger:     SetIntProp(AChildNode.ValueAsString);
  1083.       tkChar:        SetCharProp(AChildNode.ValueAsString);
  1084.       tkSet:         SetSetProp(AChildNode.ValueAsString);
  1085.       tkEnumeration: SetEnumProp(AChildNode.ValueAsString);
  1086.       tkFloat:       SetFloatProp(AObject, PropInfo, AChildNode.ValueAsFloat);
  1087.       tkString,
  1088.       tkLString:     SetStrProp(AObject, PropInfo, AChildNode.ValueAsString);
  1089.       {$IFDEF D6UP}
  1090.       tkWString:     SetWideStrProp(AObject, PropInfo, AChildNode.ValueAsWideString);
  1091.       {$ENDIF}
  1092.       tkClass:       SetObjectProp(AChildNode.ValueAsString);
  1093.       tkMethod:      SetMethodProp(AChildNode.ValueAsString);
  1094.       tkVariant:     SetVariantProp(AChildNode.ValueAsString);
  1095.       tkInt64:       SetInt64Prop(AObject, PropInfo, AChildNode.ValueAsInt64);
  1096.       end;//case
  1097.     end else begin
  1098.       // Set Default value
  1099.       case PropType^.Kind of
  1100.       tkInteger:     SetOrdProp(AObject, PropInfo, PPropInfo(PropInfo)^.Default);
  1101.       tkChar:        SetOrdProp(AObject, PropInfo, PPropInfo(PropInfo)^.Default);
  1102.       tkSet:         SetOrdProp(AObject, PropInfo, PPropInfo(PropInfo)^.Default);
  1103.       tkEnumeration: SetOrdProp(AObject, PropInfo, PPropInfo(PropInfo)^.Default);
  1104.       tkFloat:       SetFloatProp(AObject, PropInfo, 0);
  1105.       tkString,
  1106.       tkLString,
  1107.       tkWString:     SetStrProp(AObject, PropInfo, '');
  1108.       tkClass:
  1109.         begin
  1110.           PropObject := TObject(GetOrdProp(AObject, PropInfo));
  1111.           if PropObject is TComponent then
  1112.             SetOrdProp(AObject, PropInfo, 0);
  1113.         end;
  1114.       tkMethod:
  1115.         begin
  1116.           Method := TypInfo.GetMethodProp(AObject, PropInfo);
  1117.           Method.Code := nil;
  1118.           TypInfo.SetMethodProp(AObject, PropInfo, Method);
  1119.         end;
  1120.       tkInt64:       SetInt64Prop(AObject, PropInfo, 0);
  1121.       end;//case
  1122.     end;
  1123.   end;
  1124. end;
  1125. { THackComponent }
  1126. procedure THackComponent.SetComponentState(const AState: TComponentState);
  1127. type
  1128.   PInteger = ^integer;
  1129. var
  1130.   PSet: PInteger;
  1131.   AInfo: PPropInfo;
  1132. begin
  1133.   // This is a "severe" hack in order to set a non-writable property value,
  1134.   // also using RTTI
  1135.   PSet := PInteger(@AState);
  1136.   AInfo := GetPropInfo(THackComponent, 'ComponentState');
  1137.   if assigned(AInfo.GetProc) then
  1138.     PInteger(Integer(Self) + Integer(AInfo.GetProc) and $00FFFFFF)^ := PSet^;
  1139. end;
  1140. initialization
  1141.   {$IFDEF TRIALXML}
  1142.   ShowMessage('ObjectToXml demo.'#13#10'For more information please visit:'#13#10 +
  1143.     'http://www.simdesign.nl/xml.html');
  1144.   {$ENDIF}
  1145. end.