Pod článkem s anketou ohledně nejvyšší používané verze jsou i dva komentáře stěžující si na problémy s XE2. Leoš si stěžoval na design mód a stabilitu ohledně IBX komponent a JaroB na problémy s portací TinyDB z XE do XE2. Bohužel Leoše v tom asi musím nechat, jelikož IBX komponenty a já se nemáme rádi, ale ohledně TinyDB jsem si řekl, že bych to mohl zkusit.
Výsledkem byl zajímavý možný problém ohledně TStream.
JaroB udržuje haldu komponent a je velmi schopný. Jenže člověk někdy pak už trpí slepotou když se v něčem párá moc dlouho (vlastní zkušenost). Takže jsem mu napsal ať pošle testovací projekt a že zkusím štěstí. Pokud Vás nezajímají podrobnosti tak skočte rovnou na řešení.
Problém byl, že nefungovala podpora BLOB - výsledek byl prázdný stream dat s nulovou délkou, resp. při AsString to vracelo prázdný řetězec.
Nevěřil jsem, že by byl problém v kompilátoru - zvláště když se jednalo o 32bit verzi - ta je prý prakticky stejná jako v XE. Kdyby byl problém s 64bit kompilátorem tak bych i možná o tom uvažoval (pár minoritních problémů se opravovalo - upravuje). Takže zbýval problém zarovnání (nebyl jsem si jist, ale přišlo mi, že se tam s tím nějak zašachovalo), velikost nějaké struktury (kvůli 64bit se opravovalo x datových struktur, které jsou v předchozích verzích deklarovány nekorektně - ale tady kompilátor celkem spolehlivě hlásí varování nebo chyby) nebo nějaká změna ve VCL/RTL (také k vůli 64bitům).
Po ověření, že to tedy opravdu nefunguje jsem aplikoval klasický postup:
- vypnutí optimalizací
- zapnutí DEBUG dcu
- vypnutí inline (to mne napadlo až po chvilce, když jsem stále nemohl krokovat kritickou část)
- souběžné krokování v XE
procedure TTinyBlobStream.LoadBlobData;
var
RecBuf: TRecordBuffer;
BlobStream: TMemoryStream;
begin
if FDataSet.GetActiveRecBuf(RecBuf) then
begin
Inc(RecBuf, FDataSet.GetFieldOffsetByFieldNo(FFieldNo));
BlobStream := PMemoryStream(RecBuf)^;
if Assigned(BlobStream) then
begin
Clear;
CopyFrom(BlobStream, 0);
Position := 0;
end;
end;
end;
A tady jsem v podstatě propadl částečnému zoufalství. BlobStream po přiřazení nic neobsahoval (resp. velikost byla 0) a přece se pokoušel z něho kopírovat. Bez úspěchu.
V souběžném krokování v Delphi XE, ale s úspěchem (ale tato situace byla jen poprvé, pak už před voláním 0 nebyla). Zde jsem se na nějakou dobu zasekl až mne napadlo vypnout inline a důsledně krokovat.
function TStream.CopyFrom(Source: TStream; Count: Int64): Int64;
const
MaxBufSize = $F000;
var
BufSize, N: Integer;
Buffer: PByte;
begin
if Count = 0 then
begin
Source.Position := 0;
Count := Source.Size;
end;
Result := Count;
….
A tady bych měl vyhráno, kdybych nebyl medvědem. Dal jsem si totiž breakpoint na řádek s Count := Source.Size;, kde v XE2 se vracela 0, kdežto v XE hodnota. Problém byl v řádku Source.Position := 0;
Position je totiž property a volá Seek a tato metoda byla předefinována
function TOptimBlobStream.Seek(Offset: Integer; Origin: Word): Longint;
begin
LoadBlobData;
Result := inherited Seek(Offset, Origin);
end;
LoadBlobData navíc kontroluje zda je třeba data nahrávat (cache) což úžasně komplikovalo ladění. Jenže proč to v XE2 nefunguje?
Řešení
Třída TStream byla v XE2 rozšířena o 64bit verzi Seek. Přičemž 64bit varianta volá defaultně 32bit variantu a naopak pokud je to třeba.
TStream = class(TObject)
…
function Seek(Offset: Longint; Origin: Word): Longint; overload; virtual;
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; virtual;
Vše funguje v případě, že predefinujete buďto 32bit nebo 64bit a nebudete volat původní implementaci.
A protože TCustomMemoryStream využívá nově 64bit variantu a TOptimBlobStream předefinovává 32bit variantu (a volá základní třídu) je problém. Řešením ve 2 hod. ráno bylo přidat stejnou implementaci i pro 64bit verzi, ale teď si myslím, že by správně bylo jen použít 64bit variantu.