|
|
| 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.