Thursday, February 21, 2008

Rave Report, PDF and Intraweb - Felix John COLIBRI

  • abstract : how to produce PDF reports, and have an Intraweb site generate and display .PDF pages, with multi-user access
  • key words : Rave Reports - .PDF report - Intraweb site - DirectDataView - DriverDataView - Critical Section - Session management
  • software used : Windows XP Home, Delphi 2006, Rave Report BE 6.5, Intraweb 8
  • hardware used : Pentium 2.800Mhz, 512 M memory, 140 G hard disc
  • scope : Delphi 7, Delphi 8 Delphi 2005, 2006, Turbo Delphi, Turbo 2007, Rad Studio 2007
  • level : Delphi developer
  • plan :

1 - Rave Report, PDF and Intraweb

Intraweb is a very efficient tool for building web sites. Applications, mainly in the business area, also require some report output. And with the current versions of Delphi, Rave Report is the candidate of choice.

This article will present how to build Rave reports in PDF format from an Intraweb page.



2 - Rave and PDF reports

2.1 - simple Rave Report

Let's first create a simple Rave report hooked to some Table. We will use the EMPLOYEE Table, and to keep the data source simple, we will use the .XML file.

Therefore:

   create a path for the project, and copy the EMPLOYEE.XML file from:

 
C:Program Files\Common Files\Borland Shared\Data\

to a _DATA folder near your project folder

   create a new Delphi Win32 application, with "File | New | Vcl Application Win32", and save it in your application folder
   from the Tools Palette "Data Access" tab, drop a tClientDataSet on the Form
   set its FileName property to the path and file name of the EMPLOYEE.XML file, and toggle Active to True

   drop a tDataSource on the Form, and connect its DataSet to ClientDataSet1
   from the "Data Controls" tab, drop a tDbGrid and connect its DataSource to DataSource1
   the EMPLOYEE data is displayed

dataset_setup

Note that the tDbGrid is not necessary for the rest of this project, but allows us to easily check that everything is correctly initialized.



Now drop the Rave components on the Delphi side:

   from the Rave tab, drop a tRvProject on the Form
   then drop a tRvDatasetConnection, and connect its DataSet connection to ClientDataset1


2.2 - Using the Rave Designer

To build our report, we will use the Rave Designer
   double click on the RvProject1
   the Rave Designer is displayed

rave_designer

For those unfamiliar with the Rave Designer:
  • 1 is the menu
  • 2 the toolbar
  • 3 the Palette
  • 4 the Object Inspector
  • 5 the design area
  • 6 the component Treeview
Also note that the Rave Designer is a separate .EXE (which can be launched from Delphi's BIN directory), which has a life of its own. It can be used independently from Delphi, and when we build reports we will have save the report in a file with a .RAV extension, and we will have to reference this file in Delphi to use this report layout.



We will create the Dataview which allows us to get access to our tDataSet:

   select "File | New"
   a new rave design project is created
   select "File | New Data Object"
   a dialog with different data view connections is presented
   select "Direct Data View"
   a dialog with the only tRvDataSetConnection available, which is RvDataSetConnection1, is displayed
   select this connection and click "Finish"
   a new DataView1 is added to the TreeView "DataViewDictionary", with all the fields of ClientDataSet1


We will build a report layout with the rows of this DataView1:
   from the Designer Palette, select "Report | Region Component" and drop it in the middle of the designer area
   select a DataBand, and drop it on the Region1
In the Object Inspector
   set its DataView property to DataView1
   select its BandStyle property and click on the ellipsis ...
   a Band Style Editor is displayed
   check the "Detail" checkbox and click "Ok"
   in the TreeView, open the DataView1 node, to display all the fields
Then
   Ctrl-Drag-and-Drop (push Control, click on a field, drag and drop it with Control still pushed) some fields from the TreeView to the narrow white band of the DataBand
   the Rave Designer looks like this

new_rave_report



To check that everything is correctly setup, we will preview the result:
   type F9 to preview the result
   an "Output Options" dialog is displayed
   select "Preview" and click "Ok"
   the preview is displayed

raver_report_preview



And now the more important step before we leave the report, we must save this report layout in a .RAV file:
   select "File | Save"
   a "Save Dialog" is presented, usually with a path totally unrelated to our current Delphi project, like the path you used 3 weeks ago for another Rave Designer project
   navigate to our Delphi folder and save the report, as, for instance, R_EMPLOYEE.RAV


At this stage you can close the Rave Designer. Note however that this report is named "Report1" (at the top of the TreeView), as this name will be used later to build the .PDF file.



2.3 - PDF output report

Rave report has many possible outputs:
  • the printer, obviously
  • a .PDF file
  • a .RTF file
  • and some other like .HTML, internal Rave format (layout+data) etc


To produce a .PDF report, the steps are the following:
   go back to the Delphi project
   drop a tRvSystem component on the Form.
Then
  • to produce a .PDF report file, in its DefaultDest property select rdFile
  • to avoid the display of the "Output Options Dialog", select SystemSetups, click the + expand icon, and toggle ssAlowSetup to False
   select RvProject1
  • select its Engine property and connect it to RvSystem1
  • select its ProjectFile property, click the ellipsis ... and select the R_EMPLOYEE.RAV file
   drop a tRvRenderPDF component, and toggle Active to True (this is the default anyway)
   drop a tButton on the Form, create its OnClick event, and type the following code:

procedure TForm1.output_pdf_Click(SenderTObject);
  begin
    with RvSystem1 do
    begin
      DoNativeOutput:= False;
      RenderObject:= RvRenderPDF1;
      OutputFileName:= 'my_report.pdf';
    end// with RvSystem1

    RvProject1.Execute;
  end// output_pdf_Click

   compile, run, click "output_pdf"
   the MY_REPORT.PDF is saved in the current directory (where the .EXE is)
   click on the MY_REPORT.PDF file
   Acrobat is loaded, and its content displayed:

pdf_report



3 - Intraweb Rave Report

3.1 - The Intraweb Project

Let's setup the basic Intraweb site. We will use a standalone project, which avoids the IIS configuration steps. Therefore:
   create a new folder
   start a new Intraweb project by selecting "File | New | Other | Delphi Projects | Intraweb | Intraweb Application"
   the Intraweb "Application Wizzard" is displayed
   keep its default values ("Standalone Application" and "Create User Session") and in the "Project Directory" copy the name of your folder
   save this project
   compile and run it
   the project's Server is displayed
   type F9
   the browser is displayed with an empty page
The firewall will eventually block the loading of the browser, but accepting this new page will unlock this


We will place a tDataset and a tIwDbGrid on our page:
   from the Tools Palette "Data Access" tab, drop a tClientDataSet on the Form
   set its FileName property to the path and file name of the EMPLOYEE.XML file, and toggle Active to True

   drop a tDataSource on the Form, and connect its DataSet to ClientDataSet1
   from the "IW Data" tab, drop a tIwDbGrid on the Form, and connect its DataSource to DataSource1
   compile, run, type F9
   the page displays the content of the Table:

iwdbgrid



3.2 - Displaying a .PDF file

Second step: how to present a .PDF file by clicking on an Intraweb link or button.

The main points are

  • create a FILES/ sub directory and place the .PDFs in this folder
  • create a pop-up web page by code which will display the .PDF report
Therefore
   create the FILES/ sub directory in the folder containing your Intraweb .EXE
   copy the previous .PDF, in our case MY_REPORT.PDF into this folder
   from the "IW Standard" tab, add a tIwButton to the Form, create its OnClick event, and use the following code:

procedure TIWForm1.displaypdfClick(SenderTObject);
  var l_pdf_file_namestring;
      l_popup_page_namestring;
      l_popup_page_optionsstring;
      l_pdf_urlstring;
  begin
    l_pdf_file_name:= 'my_report.pdf';

    l_popup_page_name:= 'the_report';
    l_popup_page_options:= 'scrollbars=yes,width=400,height=600';
    l_pdf_url:= WebApplication.AppURLBase'/files/'l_pdf_file_name;

    AddToInitProc('NewWindow("'
        + l_pdf_url
        + '","'l_popup_page_name
        + '","'l_popup_page_options
        + '");');
  end// displaypdfClick

   compile, run, click F9, click "displaypdf"
   the report is displayed in a pop-up page:

display_pdf_page

display_pdf_page



3.3 - Generating the Report

To generate a simple .PDF report, we will use the same technique as the one used with a simple Win32 project, using a tRvProject, tRvDataSetConnection, tRvSystem, tRvRenderPdf:
   from the "Rave" tab, drop the tRvProject, tRvDataSetConnection, tRvSystem, tRvRenderPdf and connect them exactly as in the previous example

   drop a tIwButton, and, in its OnClick event, create the .PDF file in the FILES/ subdirectory:

procedure TIWForm1.CreatePdfClick(SenderTObject);
  begin
    with RvSystem1 do
    begin
      DoNativeOutput:= false;
      RenderObject:= RvRenderPDF1;
      OutputFileName:= 'FILES\my_report_3.pdf';
    end// with RvSystem1

    RvProject1.Execute;
  end// CreatePdfClick




3.4 - Multi User .PDF generation

The previous approach works, but since Rave is not thread save, when several users want to generate .PDF files, we run into race problems.

The solution recommended on the Intraweb web site is to protect the generation of the report with some synchronizing object, like a critical section, and to create the tRvDataSetConnection for each report.



Let's try this solution. There are several points to take care of

  • the critical section is created in the Initialization section of the Unit, and freed in the Finalization section
  • if the reports are confidential, we must ensure that one User cannot read the .PDF build for another User. The easiest way to do this is by creating separate folders, using the IntraWeb SessionId, which is a "non-guessable" value. During a Session, this value is retrieved using WebApplication.AppID.
  • this User directory must be created before the .PDF is generated, and deleted after the use of the .PDF. We can use the tIwServerControllerBase.OnCloseSession event to perform this


Here is our project:
   copy the previous project to a new folder, and remove the two tIwButtons and their events
   in the Project Manager, select the ServerController, and in the Object Inspector, toggle IwServerController.AllowSubFolder to True
   declare a global tCriticalSection variable, initialize it and free it in the Initialization and Finalization section of the Unit:

unit u_irp_safe_page;
  interface
    // -- ...ooo...

  implementation
    uses SyncObjs;

    {$R *.dfm}

    var // -- critical section to serialize the .PDF generation
        g_c_rave_critical_sectionTCriticalSectionNil;

    // -- ...ooo...

    initialization
      TIWForm1.SetAsMainForm;
      g_c_rave_critical_section:= TCriticalSection.Create;
    finalization
      g_c_rave_critical_section.Free;
    end

   drop a tIwButton and create the OnClick event which will generate and display the .PDF report

procedure TIWForm1.GenerateAndDisplayClick(SenderTObject);
  var l_pdf_file_namestring;
      l_session_id_segmentString;

  procedure generate_pdf;
    var l_c_rave_dataset_connectionTRvDataSetConnection;
    begin
      ClientDataSet1.Close;
      ClientDataSet1.IndexFieldNames:= g_sort_column;
      ClientDataSet1.Open;

      RVProject1.ProjectFile:=
          WebApplication.ApplicationPath'..\_data\r_employee.rav';

      with RvSystem1 do
      begin
        DoNativeOutput:= false;
        RenderObject:= RvRenderPDF1;
        OutputFileName:= IWServerController.FilesDir
            + l_session_id_segment'\'l_pdf_file_name;
      end// with RvSystem1

      // -- create the user specific path
      ForceDirectories(ExtractFileDir(RvSystem1.OutputFileName));

      // -- enter into the critical section
      g_c_rave_critical_section.Enter;

      // -- create a new RvDataSetConnectoin
      l_c_rave_dataset_connection:= TRvDataSetConnection.Create(self);

      try
        l_c_rave_dataset_connection.Name:= 'my_rave_dataset_connection';
        l_c_rave_dataset_connection.DataSet:= ClientDataset1;

        l_c_rave_dataset_connection.Name:= 'dataset_connection_'l_session_id_segment;
        l_c_rave_dataset_connection.DataSet:= ClientDataset1;

        RvProject1.ExecuteReport('Report1');
      finally
        l_c_rave_dataset_connection.Free;
        g_c_rave_critical_section.leave;

        RvSystem1.OutputFileName:= '';
      end// try/finally
    end// generate_pdf

  procedure display_pdf;
    var l_pdf_urlString;

        l_popup_page_namestring;
        l_popup_page_optionsstring;
        l_popup_parameterString;
    begin
      l_pdf_url:= WebApplication.AppURLBase'/files/'
          + l_session_id_segment'/'l_pdf_file_name;
      IwLabel2.Caption:= l_pdf_url;

      l_popup_page_name:= 'the_report';
      l_popup_page_options:= 'scrollbars=yes,width=500,height=300';
      l_popup_parameter:= 'NewWindow("'
          + l_pdf_url
          + '", "'l_popup_page_name
          + '","'l_popup_page_options
          + '");';

      AddToInitProc(l_popup_parameter);
    end// display_pdf

  begin // GenerateAndDisplayClick
    l_session_id_segment:= WebApplication.AppID;
    l_pdf_file_name:= 'safe_report_'
        + g_sort_column'_'l_session_id_segment'.pdf';

    generate_pdf;
    display_pdf;
  end// GenerateAndDisplayClick

   in the Project Manager, create the OnCloseSession event, and erase all PDF's of this session:

procedure TIWServerController.IWServerControllerBaseCloseSession(
    ASessionTIWApplication);
  const k_path_delimiter'\';
  var l_output_file_dirstring;
      l_search_recordTSearchRec;
  begin
    l_output_file_dir:= GServerController.FilesDirASession.AppID;

    // -- CleanUp session's files directory
    if FindFirst(l_output_file_dirk_path_delimiter'*.*',
        faAnyFilel_search_record)= 0
      then
        begin
          repeat
            DeleteFile(
              l_output_file_dirk_path_delimiterl_search_record.Name);
          until FindNext(l_search_record)<> 0;
          FindClose(l_search_record);
        end;

    RemoveDir(l_output_file_dir);
  end// IWServerControllerBaseCloseSession

   compile, run, type F9, click "generate_and_display"
   the PDF is displayed in a separate page, and, if you look at the FILES/ subfolder, you will see the session folder with the .PDF in it
   close the Intraweb Server
   the session folder is removed


We also tried to generate several kinds of reports, by changing some parameters of the tDataSet: the order, filtering rows etc (but NOT the column structure, which is hard coded in the .RAV)
   drop a tIwListbox, with the list of some columns, and create the event which will sort the ClientDataSet1:

var g_sort_columnString'';

procedure TIWForm1.IWListbox1Click(SenderTObject);
  begin
    with IwListBox1 do
      case ItemIndex of
        0 : g_sort_column:= 'EmpNo';
        1 : g_sort_column:= 'LastName';
        2 : g_sort_column:= 'PhoneExt';
        else g_sort_column:= '';
      end// case

    ClientDataSet1.IndexFieldNames:= g_sort_column;
  end// IWListbox1Click

   run, select a PhoneExt sort, click "generate_and_display"
   the PDF is displayed sorted in PhoneExt order

   you may repeat the operation with an other sort order


Please note that:
  • IwServerController.AllowSubFolder only exists for Intraweb versions after 5.1
  • the code within the critical section should be kept as short as possible
  • there are many possible options for the pop-up page. We could use code like:

    const k_options=  'toolbar=no,status=no,menubar=yes,scrollbars=yes'
                    + ',resizable=yes,location=no,directories=no'
                    + ',width=550,height=230';
          k_format_mask'NewWindow("%s", "%s", "%s");';  

    AddToInitProc(Format(k_format_mask, [my_urlmy_page_namek_options));




To our big dismay, we did not succeed to start another user. The result was that the second user always received the last report produced by the first user.

So it still escapes us why we had to use a critical section with no apparent benefit.



4 - Multi User Intraweb .PDF report

4.1 - Other solutions

The newsgroups tell us that there are two other possibilities:
  • either directly create the report using code (no Rave Designer)
  • or use a special Rave DataView, the Driver Data View
We will try the second approach.

DriverDataViews are Rave Designer's DataViews which allow us to create reports directly from the Rave Designer: we do NOT need to use a tDataSet on the Form. So the Rave Designer directly reads the data from some source. Not too difficult, knowing that the Rave Designer is written in Delphi. But at the same time, we can still set the Sql request of this report from the Delphi application. And the DriverDataViews have been specially written to allow multi-user access.



4.2 - Driver Data View

Here is how to create a Rave report using a DriverDataView:
   copy any BDE Table to our _DATA folder (you alternately could use the DbDemos Alias, or use another Sql engine)

   start a new Win32 Delphi project
   drop tRvProject on it, and double click it to start the Rave Designer

   select "File | New"
   a new report is created

   select "File | New Data Object"
   the "Data Connection" dialog is presented
   select the "DataBase Connection" RadioButton and click "Next"
   a list of drivers is presented
   select "BDE" and click "Finish"
   a "Database Connections Parameters" dialog is presented
   select
  • either the "DbDemos" alias
  • or the "Driver" "STANDARD" and add the "PATH=..DATA" parameter
   select the "Test" tab and check the connection
   the "Data Vue Dictionary" now contains a Database1

   select "File | New Data Object"
   the "Data Connection" dialog is presented
   select the "Driver Data View" RadioButton and click "Next"
   the Database1 is presented
   click DataBase1
   a new "Query Advanced Designer" window is presented, with our EMPLOYEE.DB in the right pane
   drag the EMPLOYEE.DB in the "Layout" pane and click "Ok"
   the TreeView contains a DriverDataView1 dataview, with all the fields

   add a Region, DataBand, dbFields, as explained above, and preview the report

   save the report as R_DRIVER_EMPLOYEE.RAV, and close the Rave Designer


4.3 - Displaying the Report

The steps are identical as the ones with a DirectDataView, but, to prepare us for a multi-report application we will set the Sql query from Delphi:
   go back to the Delphi project
   select RvProject1 and initialize ProjectFile with R_DRIVER_EMPLOYEE.RAV
   drop a tButton, and in its Click event, and set the DriverDataView Sql request. The tDriverDataView is found using tRvProject.ProjMan.FindRaveComponent

procedure TForm1.display_report_Click(SenderTObject);
  var l_c_rave_driver_data_viewTRaveDriverDataView;
  begin
    With RvProject1 do
    begin
      Open;
      // -- get a link to the DriverDataView
      l_c_rave_driver_data_view:=
          ProjMan.FindRaveComponent('DriverDataView1'nilAs TRaveDriverDataView;

      // -- modify the Sql request
      l_c_rave_driver_data_view.Query:=
            'SELECT * '
          + '  FROM employee'
          + '  ORDER BY PhoneExt '
          ;

      Execute;
    end// With RvProject1
  end// display_report_Click

   add RVData and RvDriverDataView to the USES clause

   add RvDlBDE to the USES clause (or whatever .DLL is required by Rave to access your Sql engine)

   run, click "display_report
   the preview dialog is presented, and the preview displayed


We can also generate a .PDF report on disk:
   add another tRvProject, and initialize ProjectFile with R_DRIVER_EMPLOYEE.RAV
   add a tRvSystem component, link the RvReport2.Engine to it, and initialize DefaultDest and SystemSetups
   add a tRvRenderPdf component
   add a tButton, and write the code to generate the .PDF:

procedure TForm1.output_pdf_Click(SenderTObject);
  begin
    With RvProject2 do
    begin
      Open;

      with ProjMan.FindRaveComponent('DriverDataView1'nil)
          As TRaveDriverDataView do
        Query:=
            'SELECT * '
          + '  FROM employee'
          + ' ORDER BY PhoneExt '
          ;
    end// with RvProject2

    with RvSystem1 do
    begin
      DoNativeOutput:= False;
      RenderObject:= RvRenderPDF1;
      OutputFileName:= 'my_report_4.pdf';
     end// with RvSystem1

     RvProject2.Execute;
  end// output_pdf_Click

   run, click "output_pdf_"


4.4 - Multi User Intraweb PDF reports

And now, lets try the multi user version again:
   copy the Intraweb+Rave previous project in a new folder
   add the RVData RvDriverDataView and RvDlBDE to the USES clause
   replace the previous "GenerateAndDisplay" code with the following one:

procedure TIWForm1.GenerateAndDisplayClick(SenderTObject);
  var l_pdf_file_namestring;
      l_session_id_segmentString;

  procedure generate_pdf;
    begin
      With RvProject1 do
      begin
        ProjectFile:= WebApplication.ApplicationPath
            + '..\_data\r_driver_employee.rav';

        Open;

        with ProjMan.FindRaveComponent('DriverDataView1'nil)
            As TRaveDriverDataView do
          Query:=
              'SELECT * '
            + '  FROM employee'
            + ' ORDER BY 'g_sort_column
            ;
      end// with RvProject1

      with RvSystem1 do
      begin
        DoNativeOutput:= False;
        RenderObject:= RvRenderPDF1;
        OutputFileName:= IWServerController.FilesDir
            + l_session_id_segment'\'l_pdf_file_name;
      end// with RvSystem1

      // -- create the user specific path
      ForceDirectories(ExtractFileDir(RvSystem1.OutputFileName));

      iwLabel1.Caption:= 'not ok';
      try
        RvProject1.ExecuteReport('Report1');

        iwLabel1.Caption:= 'ok';
      finally
        RvSystem1.OutputFileName:= '';

        iwLabel1.Caption:= iwLabel1.Caption' unlocked';
      end// try/finally
    end// generate_pdf

  procedure display_pdf;
    var l_pdf_urlString;

        l_popup_page_namestring;
        l_popup_page_optionsstring;
        l_popup_parameterString;
    begin
      l_pdf_url:= WebApplication.AppURLBase'/files/'
          + l_session_id_segment'/'l_pdf_file_name;
      IwLabel2.Caption:= l_pdf_url;

      l_popup_page_name:= 'the_report';
      l_popup_page_options:= 'scrollbars=yes,width=500,height=300';
      l_popup_parameter:= 'NewWindow("'
          + l_pdf_url
          + '", "'l_popup_page_name
          + '","'l_popup_page_options
          + '");';

      AddToInitProc(l_popup_parameter);
    end// display_pdf

  begin // GenerateAndDisplayClick
    l_session_id_segment:= WebApplication.AppID;
    l_pdf_file_name:= 'multi_report_'
        + g_sort_column'_'l_session_id_segment'.pdf';

    generate_pdf;
    display_pdf;
  end// GenerateAndDisplayClick

   run, click F9, click "GenerateAndDisplay"
   select the Intraweb Server and "Tools | Copy Start URL"
   start another version of your browser (to simulate another user), paste the URL, and generate some report pages


4.5 - Rave Architecture

Here is a diagram summarizing the properties we used from Rave in our Delphi code:

rave_uml_class_diagram



The organization of the Intraweb components was already presented in our Intraweb Architecture paper.



5 - Intraweb and Rave comments

5.1 - Rave and Intraweb

The commercial presentation is proud to announce the "Rave reports from Intraweb". As we experience, this is not all that easy. We derived all our knowledge from the Delphi newsgroups. That's where you do appreciate a good newsgroup spider.

Just a couple of additions:

  • Chad Z. HOWER wrote the original DriverDataViews, to allow reports despite the thread safety problems of Rave
  • some Rave support messages mentioned that what we presented only works for Standalone Intraweb. Is is untested if we uses a .DLL (Isapi etc)

On the documentation side, both Rave and Intraweb are very much alike :

  • demo programs which are somehow sensitive to the version (if you try to run them with another Delphi version than the version used for writing the original demo might not compile). Well, that's part of standard Delphi adjustments
  • the help is quite bizare: in Object Oriented Programs, you have a list of Classes, each with Properties, Events and Methods. Both helps are not exactly organized along those lines
  • some of the .PDF manuals are not clearly dated, and it might be difficult to link whatever manual you find on the web with the components you have on your Palette
  • neither company is very eager to add external links to articles about their product. Maybe there are overflooded with articles. Or perhaps they estimate that our articles are of not good enough. Or they do not like the color of our pages ... And the same goes for adding a link to companies offering trainings for their products. Well, we will try again this time, and we will see what happens !
  • however, both companies are very present in the Delphi newsgroups, trying to help out Delphi developers stuck with their so-so documentation. So, your best alternative is to post your questions there (and certainly on their own newsgroups, but we did not visit those).


5.2 - Rave Training and Intraweb Training

We have organized several specific training sessions for Intraweb as well as for Rave, anywhere in the world (of interest, of course, if there are several developers). You may contact us at fcolibri@felix-colibri.com for more details.



6 - Download the Sources

Here are the source code files: The .ZIP file(s) contain:
  • the main program (.BDSPROJ, .DPR, .DOF, .RES), the main form (.PAS, .DFM), and any other auxiliary form
  • any .TXT for parameters, samples, test data
  • all units (.PAS) for units
Those .ZIP
  • are self-contained: you will not need any other product (unless expressly mentioned).
  • will not modify your PC in any way beyond the path where you placed the .ZIP (no registry changes, no path creation etc).
To use the .ZIP:
  • unzip the downloaded file
  • using Delphi, compile and execute
To remove the .ZIP simply delete the folder.

The Pascal code uses the Alsacian notation, which prefixes identifier by program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lass etc. This notation is presented in the Alsacian Notation paper.



Never miss a thing. Make Yahoo your homepage.

0 Comments: