Deze zomer is het zeker geen komkommertijd, als het gaat om activiteiten in de VO sectie. In dit artikel wat informatie over class layout en garbage collection van Robert van der Hulst, op de website een vervolg op Erik Visser’s sessies over Word-automation op de CttM, en introducties voor Crystal Reports, bBrowser en FAB-tools van Marc Verkade en Dick van Kooten. Niet alles staat in het magazine, veel meer details zijn te vinden op de SDGN website in de VO sectie.

Fig. 1: bBrowser in de praktijk
What’s cooking?
‘In Florida is het altijd zomer’ is de running gag aldaar. Aangezien Brian Feldman, eigenaar van GrafX en projectleider van het VO ontwikkelteam, daar woont, moet je oppassen met de interpretatie van zijn uitspraken als het gaat om planning. Vandaar dat diverse mensen argwanend nadere informatie vroegen, toen Brian liet weten dat VO 2.7 gedurende de zomer in beta is (juli) en dat het de bedoeling is de 2.7 versie laat in de zomer uit te brengen. Dat zal dus wel neerkomen op september, maar wees niet verbaasd als het nog iets langer duurt. Gelukkig hoeven we niet stil te gaan zitten wachten op die nieuwe versie. Robert van der Hulst heeft in het kader van z’n column voor de SDGN een tweede artikel geschreven met inside informatie over VO’s memory management en de garbage collector, verderop meer hierover. Erik Visser had niet voldoende spreektijd op CttM om al z’n informatie te delen, vandaar dat we op de website nog eens flink uitpakken. Naar aanleiding van onze gezellige ‘live’ sessie op de avond na de CttP zijn Marc Verkade en Dick van Kooten achter het toetsenbord gekropen om introducties te verzorgen van tools die zeer handig en productieviteitsverhogend kunnen werken.
Gelukkig hoeven we niet stil te gaan zitten wachten op die nieuwe versie
Objecten in Visual Objects: van Code to Garbage, een bijdrage van Robert van der Hulst
In dit deel van het artikel wil ik jullie wat meer vertellen over de manier waarop Visual Objects met objecten omgaat. Ik wil daarbij aandacht besteden aan de rol van:
- de Compiler
- de Linker
- de Runtime
Als voorbeeld neem ik de volgende code:
CLASS Person
PROTECT _cName AS STRING
PROTECT _nAge AS DWORD
PROTECT _cGender AS STRING
METHOD Init(cName, nAge, cGender) CLASS Person
_cName := cName
_nAge := nAge
_cGender := cGender
ACCESS Name CLASS Person
RETURN _cName
ASSIGN Name(cName) CLASS Person
_cName := cName
RETURN cName
Listing 1: een typisch stukje toepassingscode
De Compiler en klassen
Allereerst wil ik het hebben over de rol van de Compiler.
Compileren van de klassedefinities
Wanneer de compiler een klassedefinitie als hierboven tegenkomt, doet de compiler een heleboel zaken:
- Er wordt assembly code gegenereerd waarmee de klasse door de Runtime kan worden aangemaakt. Deze sourcecode maakt gebruik van de (niet gedocumenteerde) functie DeclareClass(). De parameters voor deze functie zijn o.a. de klassenaam (als symbol), de Parent klasse (ook een symbol), de grootte van elk object en een lijst van de typen en namen van de geëxporteerde IVars. De grootte van de objecten is in dit geval 20 bytes: 12 bytes voor de eigenlijke data (strings vragen 4 bytes voor een pointer in het object) en 8 bytes voor een pointer naar de VTable en een pointer naar de Class Structure.
- De VTable is een structure van pointers naar de methoden en accesses/assigns en zal worden opgebouwd door de linker indien een klasse gebruik maakt van strong typing. De Class Structure wordt door de Runtime opgebouwd.
Compileren van de (access & assign) methoden
Bij het compileren van een methode zal de Compiler vanzelfsprekend controleren of de betreffende klasse aanwezig is.
Daarnaast zal de compiler de benodigde assembly code genereren. Omdat de argumenten van de methode niet getypeerd zijn, zal de compiler conversies van usual argumenten naar het juiste type genereren. Vervolgens wordt de daadwerkelijke code uitgevoerd, en tenslotte wordt het resultaat weer vertaald naar een usual, zodat het in de vorm van een usual kan worden teruggegeven. Als het argument by reference werd doorgegeven, wordt die referentie opgeheven.
Zo ziet de assembly voor de Name assign er gedeeltelijk als volgt uit (zonder optimalisaties):
STARTUP Code
LOAD Parameter in Registers // Usual (8 byte)
CALL Usual2String // Kan error geven !
STORE Register to Ivar // 4 bytes
DEREFERENCE Parameter //
STORE RESULT // 8 bytes IN EAX/EDX CLEANUP CODE
RETURN
Tevens genereert de compiler code waarmee de methode bij het runtime systeem wordt geregistreerd, gebruik makend van de niet gedocumenteerde DeclareMethod() functie:
PUSH 1
PUSH Address method
PUSH Name
PUSH Person
CALL DeclareMethod
Het converteren van de argumenten van usual naar het juiste type en terug is natuurlijk een stuk minder efficiënt dan wanneer je de argumenten van de methoden zou typeren. Dat kun je bereiken door de methode te typeren. Daarop komen we verderop terug.
Compileren van de aanroep van de methoden.
Stel we gaan de klasse als volgt gebruiken:
FUNCTION Start
Local oPerson as Person
oPerson := Person{“Mr Data”,40,”M”}
? oPerson:Name
wait
De compiler zal dan de volgende pseudo-code genereren om het object te maken:
Push “M” // as USUAL, 8 bytes
Push 40 // idem
Push “Mr Data” // idem
Push #Person // idem
Push 4// # of Usual arguments on the stack
CALL CreateInstance()
Voor het tonen van de Person:Name Access genereert de compiler de volgende pseudo-code:
Push #Name // as SYMBOL, 4 bytes
Push oPerson // as Object, 4 bytes
Call IVarGet() // Lees property
Store Result in temp var. // 8 bytes, result = USUAL
Push temp var // as USUAL so 8 bytes
Push 1 // # of arguments for QOut
Call QOUT
Je ziet dat de compiler niet direct de access methode aanroept, maar gebruik maakt van IVarGet().
Het effect op de code van typeren van methoden
Om betere performance te krijgen en tevens over het voordeel te beschikken dat de compiler betere controle over je code geeft, kun je methoden en accesses/assigns typeren.
Dat zou er als volgt uit kunnen zien
ACCESS Name AS STRING PASCAL CLASS Person
.
.
ASSIGN Name(cName as STRING)
AS STRING PASCAL CLASS Person
.
.
Tevens moet je dan de volgende regels toevoegen aan de klasse definitie:
DECLARE ASSIGN Name
DECLARE ACCESS Name
Als je dat doet, ziet de gegenereerde pseudo-code voor de Name Assign er als volgt uit:
STARTUPCODE
STORE Parameter to IVAR // Schrijf IVar
MOVE EAX Parameter // Return waarde
CLEANUPCODE
RETURN
Je ziet dat de code een stuk efficiënter is dan hiervoor, waarbij ook nog de conversie en de dereferentie wordt uitgevoerd.
Maar er is veel meer te vertellen, er kan veel meer geoptimaliseerd worden, bijvoorbeeld door gebruik te maken van het ~ONLYEARLY pragma. Wat doet de linker, wat is er runtime aan informatie beschikbaar, en wat heeft dit voor gevolgen voor de garbage collector? In het blad hebben we geen ruimte voor dit uitgebreide en interessante artikel. Daar moet je voor naar Robert’s column op de website surfen.
Crystal Reports en VO, Marc Verkade
Elk programma heeft output. Vaak wordt het beeldscherm gebruikt om informatie weer te geven, maar veelal ook een printer. Met VO2.6 wordt standaard ReportPro 2 meegeleverd, een prima rapport generator van DataPro Inc. Vaak zal ReportPro voldoende mogelijkheden bieden om uw rapporten te kunnen genereren, maar soms zijn er meer complexe rapporten noodzakelijk of willen uw klanten zelf rapporten kunnen ontwerpen met behulp van Crystal Reports.
Ik zelf heb indertijd gekozen voor Crystal Reports, omdat ik de designer beter vind en Crystal Reports meer mogelijkheden biedt, vooral op het gebied van het exporteren van rapporten. Het is een hele klus geweest om Crystal Reports goed te integreren in ons VO framework, maar we zijn zeer tevreden met het uiteindelijke resultaat.
In de komende maanden zal ik aan de hand van de demo applicatie MC_Crystal laten zien hoe je Crystal Reports in een VO applicatie kunt integreren. Na elk artikel zal de MC_Crystal demo applicatie verder uitgebreid zijn met de functies die in dat artikel zijn beschreven.
Op de SDGN website vindt u de laatste versie van de MC_Crystal applicatie. U kunt deze applicatie inclusief bijbehorende AEF downloaden en uitpakken in de folder C:\MC_Crystal. Vervolgens kan de MC_Crystal.Aef geïmporteerd worden en kunt u de demo applicatie direct bekijken.
De eerste versie van MC_Crystal staat nu klaar op internet. Deze versie start een Crystal Report en laat deze zien in het standaard Crystal Report Preview Window. Eigenlijk is het heel simpel…
// Open the engine
sReport:=”NameReport.Rpt”
DO CASE
CASE !File(sReport)
// The report does not exist
CASE !PEOpenEngine()
// The engine is not open!
CASE (nJob:=PEOpenPrintJob(String2Psz(sReport)))==0
// The report can not be opened
CASE !PEOutputToWindow(nJob,String2Psz(sReport),
10,10,10000,10000,0,0)
// Setting the output to window failed!
CASE !PEStartPrintJob(nJob,TRUE)
// The report can not be started
CASE !PEPrintWindow(nJob,TRUE)
// The Report can not be printed!
OTHERWISE
// Yahoo, the report is printed!
END
De uitleg van de methodes en functies met betrekking tot de MC_Crystal applicatie is te vinden in het webartikel op www.sdgn.nl
BBrowser & Fab: twee waardevolle toevoegingen
Visual Objects beschikt over de databrowser voor het kolomsgewijs weergeven van veldinformatie. Gebruikt u die nog steeds? En heeft u Visual Objects 2.6? Dan is het de hoogste tijd de databrowser aan de kant te zetten. Bij uw VO 2.6 zit een rijkelijk uitgeruste, gratis versie van bBrowser, een uit Duitsland afkomstige 'third party browser'.
Een andere tool is eveneens gratis en wordt ook intensief gebruikt. Het is de FabPaintControl van de Fransman Fabrice Foray. Deze programmeur heeft op zijn website http://www.fabtoys.net een indrukwekkend aantal voor Cavo geschikte utilities gezet. En het zijn nogal wat utilities die een programmeur met gemiddelde kennis niet zomaar maken kan, zoals Fab Twain (om scans via een Twain driver in te lezen), Fab Zip (om zipbestanden te maken en te lezen) of Fab Paint.
Het nadeel van dit soort tools is dat je tijd moet besteden om ze te leren kennen en vooral om ze in je bestaande applicatie te implementeren. In een nieuwe reeks webartikelen, waarvan ondergetekende een deel gaat schrijven, proberen wij u om de valkuilen van bepaalde Cavo classes heen te loodsen, zodat u deze met minimale inspanning en eenvoudig te begrijpen voorbeeldcode kunt implementeren. Als eerste volgt een inleidend bBrowser artikel. Hiermee kunt u snel een browser opzetten en ook snel de standaard Cavo browser weergave vervangen door de bBrowser. Vooral dat laatste is een minder bekende mogelijkheid. Daarna volgt een artikel over het toepassen van de FabPaint control, de FabTwain library en de FabZip library.
Fab Paint control
De implementatie van de Fab Paint control is zeer eenvoudig. In uw applicatie schrijft u:
LOCAL oPLC AS FabPaintLibCtrl
gevolgd door
oPlc:=FabPaintLibCtrl{SELF,199,Point{},Dimension{},,TRUE)
en voor het weergeven van een bepaalde afbeelding cFile (pad+bestandsnaam):
oPlc:Image:=FabPaintLib{ cFile, SELF }
bBrowser
Hoewel bBrowser zich presenteert als een control, kunt u ook eenvoudig een tabelweergave vervangen door bBrowser. Als u een bBrowser control plaatst, doet deze op zichzelf niets. Dus gaan we wat code schrijven die we gemakkelijk kunnen gebruiken voor alle genoemde toepassingen. Om te beginnen subclassen we de bBrowser en laten we het control erven van deze subclass:
CLASS IC2bDataBrowser INHERIT bBrowser
We kunnen dan een init method schrijven. De weer te geven velden zijn eenvoudig door te geven. Zo maak ik gebruik van 3 arrays, die ik aan een zelfgeschreven method doorgeef. De array aSymFields bevat de symbolic names (dus met een # ervoor) van de direct weer te geven veldnamen. De array aSymLogicFields bevat eventueel op de gewenste plaatsen enkele extra velden, waarvan de inhoud niet rechtstreeks uit de database komt, bijvoorbeeld omdat er mee gerekend moet worden, of dat ze via een keyveld uit een andere database gehaald moeten worden. Ook de kopjes staan in een array. Een algemene method om op een scherm met adressen een bBrowser met contactpersonen te vullen zou er ongeveer als volgt uit kunnen zien:
METHOD DefineContactBrowser(oBrowser) ;
CLASS RelatieVenster
LOCAL aSymFields AS ARRAY
LOCAL aSymLogicFields AS ARRAY
aSymFields:={#Roepnaam,#Toestel}
aSymLogicFields:={#Roepnaam,#Naam,#Toestel}
SELF:oDCbBContact:Use(oas:odbContact,aSymFields)
SELF:oDCbBContact:Server:OrderScope(
TOPSCOPE,SELF:Server:groep+SELF:Server:nr)
SELF:oDCbBContact:Server:OrderScope(
BOTTOMSCOPE,SELF:Server:groep+SELF:Server:nr)
SELF:oDCbBContact:SetCaptions(aSymLogicFields,{;
{"Voornaam",90},;
{"Naam contact",200},;
{"Tel.",120},;
})
SELF:oDCbBContact:DisplayTheColumns(aSymLogicFields)

Fig. 2: bBrowser volgens voorbeeld in actie
Conclusie
Het mag duidelijk zijn: stil zitten en achterover leunen is er niet bij, behalve als je je laptop meeneemt op vakantie. De zomer zit er weer bijna op, en de nieuwe VO versie staat voor de deur. Hou de SDGN website goed in de gaten voor nieuws hieromtrent en stel je vragen naar aanleiding van de artikelen op onze nieuwsgroep.
Tot ziens op de CttP van 12 september.