Několik poznámek ohledně upgrade mezi verzemi Delphi

vložil Jaro Beneš 8. září 2011 22:47

Jaroslav Beneš zaslal trochu zkušeností ohledně upgrade projektů ze starších verzí. Kromě jiného je autorem aktuální RxLib pro Delphi XE2 nebo udržuje DelphiX atd.

Jen dobrá rada (editora): než budete sami upgradovat cizí komponenty, podívejte se zda to neudělal někdo jiný. Většina populárních komponent je nejméně v unicode verzi (ne-li rovnou v XE2) a tam skok na XE2 nebo 64bitů není tak velký jako mezi Ansi - Unicode.

Moje aplikace jsou založené na Delphi 2005/2006 (jednalo se asi o cca 50 aplikací různých typů) a v podstatě největší problém byl s knihovnami třetích stran.

Stál jsem před zásadním rozhodnutím – buď je převedu, nebo z aplikací vyrazím. S vlastním vyrážením ale nemám dobré zkušenosti, knihovny Orpheus jsem se zbavoval déle než rok.

Kromě mých autorských komponentů (většina, kromě pár neveřejných, je soustředěna zde www.micrel.cz/delphi/index.html), jsem měl dva zásadní problémy: první byl v knihovně RXLibrary, kterou používám skoro ve všech aplikacích a druhý knihovna TinyDB.

Navíc, u knihovny RXLibrary jsem provedl rekompilát a vložil či dopsal do ní i další komponenty (translátor, plug-in manažer, db item grid viewer, barevný progressbar atp.). Knihovna se osvědčila (díky více jak pěti tisícům uživatelů) a já ji převedl v podstatě 1:1 do XE/XE2.

Pokusím se ve zkratce popsat hlavní problémy, na které je možné narazit při převodu dalších balíků komponent:

1/ nekompatibilita dfm

Doporučuji mít všechny dfm v textovém tvaru (spousta uživatelů pořád používá binární tvar, ale ten se špatně porovnává). Pokud je problém, je dobré otevřít v IDE Delphi všechny formuláře (v případě knihoven samozřejmě až po nainstalování balíku do IDE), převést je na text a možné další nekompatibility obezřetně odstranit z dfm ručně (typicky jsou to množiny, v dfm jsou nedefinovaná data schopna generovat hausnumera nebo i jiné blbosti). Často dojde i k vlastní úpravě automaticky spravovaných uses a doplní se chybějící jednotky.

2/ nekompatibilita v uses

Tady jsou dva problémy

a)v runtime balících

(nejde jenom o doporučení pro deprecated, ale obecně pro odkazy na další unity v rámci balíku). Většinou zakomentuji podmíněné sekce a přidávám. Pokud je vše OK, tak direktivou např. pro XE2 {$IFDEF VER230} nebo jinou zástupnou ale stejného významu, vymezím novou část sekce viz příklad:

uses
  Windows, Registry,
  {$IFDEF RX_D15}Types,{$ENDIF} //pouze Delphi XE a vyšší
  Controls, Messages, Classes, Forms, IniFiles, Dialogs, RxVCLUtils, RxHook;

Pozn. editora: Pro Delphi XE2 je nutno počítat s tím, že designtime balíčky jsou vždy 32bit a to i v případě 64bit komponent!

V případě obecné náhrady např. Longint není podporován v XE2 v eventu, nutno nahradit viz příklad:

    function DoHelp(Command: Word; Data: {$IFDEF RX_D16}THelpEventData{$ELSE}Longint{$ENDIF};
      var CallHelp: Boolean): Boolean;

Pozn. editora: XE2 je velmi striktní ohledně typů pro WinAPI. Viz. uvedený příklad. Je to protože definovaný typ může mít různou velikost pro 32 a 64bitů, a tímto se zaručí správnost dat.

b)v designtime balících

pokud má člověk smůlu a měl by převzít experty založené na třídě TExpert tak je možná lepší je oželet (nedej bože, je-li expert součástí runtime např. je v jednom souboru – to se tak jako tak musí soubor roztrhnout) jinak se musí vyrobit zcela nový wizard viz

type
  T_new_Wizard = class(TNotifierObject, IOTAWizard, IOTARepositoryWizard,
      IOTAMenuWizard, IOTAProjectWizard)
  end;
  T_new_ProjectCreator = class(TInterfacedObject, IOTACreator, IOTAProjectCreator)
  end;
  T_new_ModuleCreator = class(TInterfacedObject, IOTACreator, IOTAModuleCreator)
  end;
  T_new_OTAFile = class(TInterfacedObject, IOTAFile)
  end;

A degenerovat všechny potřebné metody tj. přenést obsah potřebných metod do nových obálek (někde jsem viděl výraz "OTA rewrapping").

Nejsložitější je samozřejmě metoda Execute wizardu, případně generátor kódu pro novou unitu – pokud je to samozřejmě potřeba. (příklad je možné nalézt v RxPluginWizard.pas z knihovny RxLibrary, odkud jsou citované úryvky kódu).

Převod balíku na vyšší verzi

Pro převod *.dpk doporučuji vzít stávající dpk soubor od nejvyšší dostupné verze Delphi a ručně ho přejmenovat např.

rtmJBLib90.dpk a dclJBLib90.dpk (z Delphi 2005)

přejmenovat na

rtmJBLib160.dpk a dclJBLib160.dpk (do Delphi XE2)

každý z obou otevřít v poznámkovém bloku (nebo jiném editoru) a přepsat záhlaví

package DclJBLib90;

na XE2 verzi

package DclJBLib160;

Z vlastní zkušenosti doporučuji udržovat extra adresář jen pro balíčky *.dpk (je jedno, zda je nadřízený nebo podřízený, hlavně aby byl oddělený)

Verze A/ hlavní adresář se zdroji, podřízený s balíky

<adresář - zdroje>JBlib
<adresář - jen dpk>packages

Poznámka: všechny soubory v *.dpk musí ukazovat nahoru tj.

RxPictEdit in '..\RxPictEdit.pas',

Verze B/ všechno je v jednom adresáři

Verze C/ v hlavním adresáři jsou *.dpk v podřízeném zdroje

<adresář - jen dpk>JBlib
<adresář - zdroje>  units

Poznámka: všechny soubory v *.dpk musí ukazovat do adresáře units tj.

RxPictEdit in '.\units\RxPictEdit.pas',

Většinou používám variantu A, protože pak může být vedle sebe více adresářů pro různé verze Delphi viz

<adresář - zdroje>JBlib
<adresář - jen dpk pro Delphi 2005>packages_D2005
<adresář - jen dpk pro Delphi 2006>packages_D2006
<adresář - jen dpk pro Delphi 2007>packages_D2007
<adresář - jen dpk pro Delphi 2009>packages_D2009
<adresář - jen dpk pro Delphi 2010>packages_D2010

Od verze Delphi 6 existuje ještě jedna docela efektní možnost správy balíku (která ale v Delphi 2010 a XE nefungovala dobře). Pozn. nejméně v Delphi XE se dá požadované specifikovat přes Project Options / Description - Lib prefix, Lib suffix, Lib version - možná to s tím souvisí.

Přímo do balíku se vloží podmínka pro sufix, balík bude mít obecný tvar jména např. dclJBLin.dpk

{pro každou podporovanou verzi Delphi vlastní suffix}
{$IFDEF VER140} { Borland Delphi 6.x }
{$LIBSUFFIX '60'}
{$ENDIF}

{$IFDEF VER145} { Borland C++Builder 6.x }
{$LIBSUFFIX '65'}
{$ENDIF}

{$IFDEF VER150} { Borland Delphi 7.x }
{$LIBSUFFIX '70'}
{$ENDIF}

{$IFDEF VER170} { Borland Delphi 2005 9.x }
{$LIBSUFFIX '90'}
{$ENDIF}

{$IFDEF VER180} { Borland Delphi 2006, Turbo 10.x }   // JB
{$LIBSUFFIX '100'}
{$ENDIF}

{$IFDEF VER185} { Borland Delphi 2007 11.x }   // JB
{$LIBSUFFIX '110'}
{$ENDIF}

{$IFDEF VER200} { Borland Delphi 2009 12.x }   // JB
{$LIBSUFFIX '120'}
{$ENDIF}

Ale pozor, suffix se správně vepíše do *.bpl např. JBLib110.bpl ale soubor symbolů dcp zůstane bez sufixu a při souběžném využití jednoho adresáře (typicky se tak děje u Delphi 2005/2006) dochází k přepisu. Pro RAD studio 2007 a vyšší je to jedno, každá verze má vlastní adresář. Při zkušební kompilaci balíků lze odstranit chyby typu viditelnosti metod, velikost písmen u konstruktorů a destruktorů (mnoho autorů píše create, destroy s malými písmeny a Delphi hlásí hint nesouhlasu jmen), implicitní zahrnutí units nebo balíků, případně další zjevné chyby.

Poznámka k own-datasetům z ne-unicode Delphi

Kamenem úrazu při převodu jakýchkoliv potomků datasetu do unicode verze je buffer – v neunicode je obvykle PChar, ale do novějších je potřeba jako PByte (nebo alespoň PAnsiChar).

Následující příklad ukazuje, kde jednoduše pozměnit typ pro buffer, aby byl kompilovatelný v obou variantách.

V případě užití bufferu jiným způsobem (a mám na mysli osobitou pointerovou aritmetiku) tak je převod víc než problematický.

Např.

Type
  //pomocný typ, který reprezentuje unicode/nonunicode variantu
  TBuffer = {$IFDEF UNICODE}PByte{$ELSE}PChar{$ENDIF};

TXXTable = class(TDataset)

protected
  //minimálně tyto metody je obvykle potřeba pro vlastní dataset předefinovat
  //
     function AllocRecordBuffer: TBuffer; override;
     procedure FreeRecordBuffer(var Buffer: TBuffer); override;
     procedure GetBookmarkData(Buffer: TBuffer; Data: Pointer); override;
     function GetBookmarkFlag(Buffer: TBuffer): TBookmarkFlag; override;
     function GetRecord(Buffer: TBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
     function GetRecordSize: Word; override;
     procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
     procedure InternalClose; override;
     procedure InternalDelete; override;
     procedure InternalFirst; override;
     procedure InternalGotoBookmark(Bookmark: Pointer); override;
     procedure InternalHandleException; override;
     procedure InternalInitFieldDefs; override;
     procedure InternalInitRecord(Buffer: TBuffer); override;
     procedure InternalLast; override;
     procedure InternalOpen; override;
     procedure InternalPost; override;
     procedure InternalSetToRecord(Buffer: TBuffer); override;
     function IsCursorOpen: Boolean; override;
     procedure SetBookmarkFlag(Buffer: TBuffer; Value: TBookmarkFlag); override;
     procedure SetBookmarkData(Buffer: TBuffer; Data: Pointer); override;
     procedure SetFieldData(Field: TField; Buffer: Pointer); overload; override;
     procedure SetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean); overload; override;
     procedure ActivateBuffers; override;
     procedure CreateFields; override;
     function GetRecordCount: Longint; override;
     function GetRecNo: Longint; override;
 end;

pokud to není možné, tak je vhodné všechny výskyty PChar změnit na PAnsiChar, obalit místa kde dochází ke konverzi viz

ansi := Ansistring(unistr); a unistr := string(ansi);

nižší verze Delphi to akceptují, v horším případě ignorují.

Ve všech případech je ale nezbytné v podstatě řádek po řádku zdrojový kód projít a ujistit se o správnosti dle metodiky pro převod do unicode prostředí.

Prakticky

Při převodu jedné důležité aplikace jsem narazil na problém s QuickReportem. Mám to zakoupené a tak jsem se snažil převést verzi z XE na XE2. První postřeh je zajímavý v tom, že QBS (viz http://www.quickreport.co.uk), distributor QuickReportu, nebyl doposud schopen správně/vůbec převést experty v balíku na nový formát (pro Delphi 6 a vyšší !).

Druhá věc, která mě praštila do očí, je poměrně výrazná odlišnost zdrojáků - neodlišují se v závislosti verzí Delphi (unicode - neunicode), ale mají odlišné funkčnosti na úrovni základních funkcí a to i na úrovni modulu PRINTER (nastavení, rotace, parametry tiskárny atp.). Jinými slovy, funkčnosti, které jsou zabudované do verze pro Delphi 2007, neodpovídají funkčnostem zabudovaným pro Delphi XE (a ještě jinak, zdrojáky pro Delphi 2007 jsou z velké části odlišné od zdrojáků pro XE). Vypadá to tak, jakoby na tom pracovali dva různí lidé s různou koncepcí - a pak shoda na úrovni jednoho zdrojáku, přeložitelného jak unicode, tak neunicode verzí Delphi je nízká ne-li nemožná.

Tak jsem prostě vzal aktualizovanou verzi pro XE a převedl jsem ji na XE2 (bez ohledu na chybějící funkčnosti z neunicode verze). Kompatibilita byla velmi dobrá, pouze jsem musel aktualizovat pár eventů kvůli odlišné deklaraci a pár výjimek díky deprecated funkcím. To je zajímavá věc, musí se kvalifikovat názvem unity (např. DirectoryExists), protože mi nějak nefungovala přednost dle postavení v uses (tj. zleva doprava) a přehození názvu unit nestačí - ale to je asi jen nějaká anomálie.

uses Sysutils, FileCtrls; {<- prohození nefungovalo, to je ta anomálie, musí se kvalifikovat názvem unity viz níže}
…
if {$IFDEF VER220UP}SysUtils.{$ELSE}FileCtrl.{$ENDIF}DirectoryExists(FDir) then …

Znamená to, že pokud je na řádku pouze a jen „if DirectoryExists(FDir) then …“ tak to vždycky ohlásí deprecated pro DirectoryExists() (použije se verze z FileCtrls).

Protože stále používám RXLibrary, tak jsem tuto knihovnu aktualizoval na XE2 (už je to odpublikováno na torry.net). Ve své podstatě jsem aktualizoval pouze šest unit (z nichž nejmenší bylo doplnění direktiv pro VER230). Některé úpravy nemusí být úplně OK, hlavně kvůli mixu s assemblerem, ale to prověří čas.

Překlad hlavní aplikace dopadl velmi dobře, ale musím konstatovat, že pochází-li aplikace z nějaké nižší verze Delphi (u mě je to nejníže z Delphi 2005), tím je víc práce s vlastním převodem. pozn. editora: Velmi významná změna je mezi Delphi 5 a 6, hlavně ohledně Variants a ukládání ne ASCII znáků v DFM*.

Zapomněl jsem podotknout něco k mojí používané databázi tj. TinyDB - převedl jsem ji na 100% bez jakéhokoliv problému, a zdá se mi, asi i díky drobným optimalizacím, i poněkud rychlejší. Používám jí jako náhradu za logy a ini soubory, a taky proto že vyhovuje zákonu č. 101/2000 Sb. o ochraně osobních dat, příp. zákonu č. 525/2004 Sb. a je od r. 2006 open source. Má jedno velké minus, nemá SQL podporu, ale ta lze suplovat skrz xQuery (což je už taky open source).

Tagy: ,

Praxe

Komentování ukončeno

Naše nabídka

Partial English version.

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Anketa


Poslední komentáře

Comment RSS