|
|
Line 28: |
Line 28: |
| Simply add wininet.lib to your project (file can be found in \lib directory of your C++ Builder / BDS / CRS installation). In recent IDEs you can also add<br> | | Simply add wininet.lib to your project (file can be found in \lib directory of your C++ Builder / BDS / CRS installation). In recent IDEs you can also add<br> |
| <code>#pragma link "wininet.lib"</code><br> in your code. | | <code>#pragma link "wininet.lib"</code><br> in your code. |
− |
| |
− | ==NG Serialization==
| |
− | ===How to serialize collections===
| |
− | Serializing collections, including custom or standard collections (like TList) requires writing custom converters. For example, lets consider the folowing collection declaration:
| |
− |
| |
− | <pre class="brush:delphi">
| |
− | type
| |
− | TItem = class
| |
− | public
| |
− | X: Integer;
| |
− | Y: Integer;
| |
− | end;
| |
− |
| |
− | TItems = class(TObjectList<TItem>);
| |
− | </pre>
| |
− |
| |
− | To be able to serialize it, we need to declare a converter class, and '''associate''' this class with our collection using Converter attribute:
| |
− |
| |
− | <pre class="brush:delphi">
| |
− | type
| |
− | TItemsConverter = class(TConverter)
| |
− | public
| |
− | function GetReadMode: TReadMode; override;
| |
− | procedure Write(S: TSerializer; const V); override;
| |
− | procedure Read(D: TDeserializer; var V); override;
| |
− | end;
| |
− |
| |
− | [Converter(TItemsConverter)]
| |
− | TItems = class(TObjectList<TItem>);
| |
− | </pre>
| |
− |
| |
− | The converter can be implemented like this:
| |
− |
| |
− | <pre class="brush:delphi">
| |
− | function TItemsConverter.GetReadMode: TReadMode;
| |
− | begin
| |
− | Result := rmFillRead; // Indicate fill-read mode.
| |
− | end;
| |
− |
| |
− | procedure TItemsConverter.Read(D: TDeserializer; var V);
| |
− | var
| |
− | lst: TItems;
| |
− | begin
| |
− | lst := TItems(V); // Since we work in fill-read mode,
| |
− | // V already contains a reference to
| |
− | // TItems instance; we should not
| |
− | // create it here.
| |
− | lst.Clear;
| |
− |
| |
− | D.BeginArray;
| |
− | while D.HasNext do
| |
− | lst.Add(D.Value<TItem>);
| |
− | D.EndArray;
| |
− | end;
| |
− |
| |
− | procedure TItemsConverter.Write(S: TSerializer; const V);
| |
− | var
| |
− | lst: TItems;
| |
− | i: Integer;
| |
− | begin
| |
− | S.BeginArray;
| |
− | lst := TItems(V);
| |
− | for i := 0 to lst.Count - 1 do
| |
− | S.Value(lst[i]);
| |
− | S.EndArray;
| |
− | end;
| |
− | </pre>
| |
− |
| |
− | As can be seen, GetReadMode returns rmFillRead, which indicates that de-serializer should use fill-read mode. This means that no new TItems instance is created during de-serialization. This mode is especially usefull for collection like classes, because they usually owned by some other objects. For example, lets look at the following class:
| |
− |
| |
− | <pre class="brush:delphi">
| |
− | type
| |
− | TOwner = class
| |
− | private
| |
− | FItems: TItems;
| |
− | public
| |
− | constructor Create;
| |
− | destructor Destroy; override;
| |
− | property Items: TItems read FItems;
| |
− | end;
| |
− |
| |
− | constructor TOwner.Create;
| |
− | begin
| |
− | inherited Create;
| |
− | FItems := TItems.Create;
| |
− | end;
| |
− |
| |
− | destructor TOwner.Destroy;
| |
− | begin
| |
− | FItems.Free;
| |
− | inherited;
| |
− | end;
| |
− | </pre>
| |
− |
| |
− | As can be seen, TItems collection is oqned by TOwner class. TOwner expose Items as read-only property, and thus, there no way to replace collection as a whole. Only collection content can be changed.
| |
− |
| |
− | But, fortunately, TOwner class can be serialized and de-serialized, because TItems collection has fill-read mode converter:
| |
− |
| |
− | <pre class="brush:delphi">
| |
− | var
| |
− | src, dst: TOwner;
| |
− | stream: TStream;
| |
− | s: TBinarySerializer;
| |
− | d: TBinaryDeserializer;
| |
− | begin
| |
− | stream := TMemoryStream.Create;
| |
− | try
| |
− | src := TOwner.Create;
| |
− | src.Items.Add(TItem.Create);
| |
− | src.Items.Add(TItem.Create);
| |
− | src.Items.Add(TItem.Create);
| |
− |
| |
− | s := TBinarySerializer.Create(stream);
| |
− | s.Value(src);
| |
− | s.Free;
| |
− |
| |
− | stream.Position := 0;
| |
− |
| |
− | d := TBinaryDeserializer.Create(stream);
| |
− | dst := d.Value<TOwner>;
| |
− | d.Free;
| |
− | finally
| |
− | stream.Free;
| |
− | end;
| |
− | end;
| |
− | </pre>
| |
− |
| |
− | Please use the following link for more information about fill-read mode:
| |
− | [http://www.lmd.de/downloads/tutorials/serialization/index.html?fill-read_mode.htm Fill-Read Mode]
| |
Q: I was trying out the LMD RTF wrapper (using BCB 6) and wanted to know what would be the best way to search for words or characters with a specific attribute.
A:
Simply add wininet.lib to your project (file can be found in \lib directory of your C++ Builder / BDS / CRS installation). In recent IDEs you can also add
#pragma link "wininet.lib"
in your code.