Skip to content

Advanced Windows Programming (Lescasse)

Vector 13:4, page 63

reviews system interfaces

Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 63

Overview

If you have just bought Delphi and want to start using it immediately with APL then this is the book for you. It is written in the style of a recipe book with very simple step by step examples showing you how to create a dish of Delphi DLLs which can be called from APL.

Every example gives APL code for Dyalog APL/W Version 8 and for APL+Win Version 2.0 (Beta). The primary platform is Delphi 2 under Windows 95 running 32-bit code, but there are examples of 16-bit code using Delphi 1 so that older versions of APL can be used under Windows 3.x.

The coverage is quite complete. Pretty well all of the fundamental ways in which you can make good use of a DLL are touched on in the examples. However, there is little that shows you how to progress from the very simple to the more complex routines that would be actually required in practice.

As all but one of the examples are so very simple, the word "Advanced" in the title is rather misleading. A more informative title would have been "A Delphi Primer for APL Programmers".

First Impressions

Last year I bought Delphi as it appeared to be a well designed GUI product which would complement the power of APL, or for that matter J. However, I never seemed to have time to get to grips with using it in the way I had intended. So, as soon as the book arrived I enthusiastically started on Chapter 1 which shows how you can use the powerful Delphi form designer to create a form which can then be translated into either a Dyalog APL or APL+Win function. I started to faithfully follow the step by step procedure:

  1. start Dyalog APL/W 8
  2. load the workspace which contains the ParseDFM and ParseDFMObject functions

Step 1 — easy. Step 2 — what workspace? I loaded the only workspace, BOOK, on the companion diskette and there was no sign of any Parse functions. Now, I have to confess that I had skipped the introduction, which is usually just blah, blah, blah, to get my hands on the interesting bits. So I retraced my steps to carefully read every word in case there was some warning that you had to pay extra for this particular goodie. But no, the only direct reference was a little footnote stating that the parser tool is currently only available for Dyalog APL/W 7 and 8. Consulting the Vector Product Guide disclosed that UNIWARE sells the Delphi Forms Translator for $195.

First impressions were therefore of utter disgust. Here was a tutorial where you cannot even get started without spending a whole lot more money. However, having skipped over Chapter 1, the remainder of the book contains a lot of useful information on how to make good use of the combined power of APL and Delphi. If Eric had placed the chapter at the end of the book and had had the grace to issue a warning that you had to purchase the software to make it work, then first impressions would have been very different.

This review therefore starts at Chapter 2 and leaves a description of the Forms Translator to the end.

Non-GUI DLLs with Delphi

Eric describes in detail, and with great clarity, how to create the simplest possible DLL in Delphi and then shows you how you can call it both from Dyalog APL and from APL+Win.

Creating a Delphi DLL is simply a matter of selecting File/New then clicking on the DLL Object icon. Delphi creates a template containing the basic code required for a DLL. All you have to do is to type in your code. The following is Eric’s first example where I only had to type the middle 6 lines defining the function intArg and declare it in the exports clause:

library firstdll;

uses
  SysUtils,
  Classes;

function intArg(X: Integer): Integer; stdcall;
begin
  intArg:=X div 2;
end;

exports
  intArg index 1;

begin
end.

In both APLs the DLL is accessed using ⎕NA. The DLL took about 5 seconds to complete, but I spent ages fiddling about trying to get ⎕NA to work. In Dyalog APL/W Eric gives the following example:

⎕NA'I4 T:\DYALOG95\BOOK\FIRSTDLL.DLL.C32|intArg I4'

but because my DLL was in a different directory I typed:

⎕NA'I4 c:\vector\review\delphi\firstdll.dll.c32|intArg I4'

and got a DOMAIN ERROR. In the end I found that the file name can be in upper or lower case, but the C32, which indicates the 32 bit C calling convention, must be upper case.

The example for APL+Win was:

'DLL I4 ← FIRSTDLL.intArg(I4)' ⎕na 'intArg'

which I typed in faithfully but got a 0 return code indicating that ⎕NA had not worked. Examination of the help for ⎕NA disclosed an example with no spaces around the assignment arrow. When I entered the left argument as 'DLL I4←FIRSTDLL.intArg(I4)' then everything worked fine. APL+Win does not seem to mind if the key word DLL or the DLL name are in upper or lower case. The difference could be due to the my using Version 1.8 rather than Version 2.0 (Beta) which was used for the book, but I am a little suspicious as the book is full of minor typing errors.

Once these little hurdles were surmounted everything went very smoothly. I was grateful to have Eric’s book in front of me assuring me that everything ought to work and all that might be wrong was a typing error.

The chapter contains detailed step by step descriptions of every possible way in which scalars and arrays of various types can be passed to, and updated by, a DLL. Everything is simply and clearly explained and is a useful tutorial for a beginner in Delphi as well as understanding how to link APL with DLLs. I was interested in the technique of switching off Delphi’s array bound checking so that a pointer to an array can be passed to the DLL. Obviously this could result in some spectacular errors as in APL one gets to rely on INDEX ERROR coming up when one makes a stupid mistake. Particular care will be needed in any Delphi DLL code when switching off the equivalent facility.

The chapter ends with a description of a utility DLL containing functions which carry out tasks that are difficult or impossible to carry out in APL. There is a function to create a new directory which would be useful for Dyalog APL/W 8 users as this facility does not seem to be directly available, unlike APL+Win which has ⎕MKDIR. Another function returns the day of week of a date. This is reasonably easy in APL, and most people create APL utilities which carry out date manipulation tasks, but why bother to create an APL utility when Delphi has the function built in?

GUI DLLs with Delphi

The tutorial part of this chapter shows you how to create a Delphi form in a DLL and call it from APL. The example form is populated with a set of controls which require all the data types discussed in the previous chapter to be passed to and from the form, so there is an easy learning curve based on previous experience.

The controls on the example form include an edit field and a memo field, both of which are populated from APL character vectors. As these controls allow you to type in any amount of text there is a problem should you type in any additional characters which would extend the length of the original APL vector. For example, if you pass in a pointer to V where ⍴V is 10, then you cannot enter any more than 10 characters. The 11th character will overwrite memory in APL and will normally cause a GPF.

I was not very happy with Eric’s suggested solution to the expanding character vector problem. He suggests that you pad out your character vector with spaces and insert and Ascii zero (⎕AV[⎕IO]) at the end of the text that is to appear in the control. For example, you could create a vector with A←256↑'Text', ⎕AV[⎕IO] then this allows the user to type a total of 255 characters, allowing room for a trailing ⎕AV[⎕IO]. The main problem with this solution is that the Delphi code does not know the overall length of your character vector and therefore cannot stop the user from typing too many characters by setting the MaxLength property of the control. The technique used by the ODBC driver interfaces seems to be much cleaner. There are usually three pointers: a pointer to the character string for the initial text, a pointer to the buffer to be filled by the function, and a pointer to the length of the buffer. On return the pointer to the buffer length contains the actual length of the string placed in the buffer.

The chapter ends with some rather nice examples of simple forms that could be called from APL. All these forms must, or course, be modal. APL has to be in control and must wait until the user has completed entering data on the form before continuing. There is no way at present for Delphi to be in control and to call APL as a DLL; you need J for that.

Creating a Resources DLL

Not only can DLLs contain functions and forms but they can also contain bitmaps. As Eric points out you can easily accumulate a couple of hundred small bitmaps to display on the toolbars of an APL application. Storing the bitmaps in a DLL rather than as separate files on disk makes a lot of sense.

Bitmaps, icons, cursors, fonts and text constants can all be stored in a Resource file which can then be included in your DLL. The Borland Resource Workshop is a powerful tool for maintaining Resource files and is very simple to use. There is also an Image Editor which is not nearly as powerful and is that much more difficult to use. The detailed instructions on how to use both systems seem a little over the top, but do get you going quickly.

The really useful information as far as I was concerned was the instructions on what you have to do to load a bitmap into APL+Win. This requires the use of ⎕WCALL to run the Windows API functions LoadLibrary and LoadBitmap. It was also useful to be informed of the size of the bitmap required for toolbar buttons, which is slightly different in Dyalog APL/W and APL+Win.

The chapter ends with an example of a little APL form displaying a toolbar (or toolbox) with bitmaps loaded from the DLL.

Other Delphi Techniques

The main topic of this chapter is an explanation of how to display a splash screen while APL is loading.

The trick is to run a tiny Delphi program which immediately displays a splash screen. It then reads a .INI file and parses the command line string to see which APL workspace is to be loaded. The Delphi program issues the appropriate call to load the workspace and then goes into a wait loop checking to see when APL is ready. As soon as the wait loop terminates the Delphi program closes down.

I was impressed with this application. It appeared to be the live code for Uniware Toolkit/W and must be very near to code which is actually delivered to customers. This is the one topic in the book which merits the word ‘Advanced’ in the title.

The other example in the chapter, how to play a .WAV sound file in Delphi, seems just too simple this far into the book. Also, you can perfectly well play a .WAV file directly from APL.

Accessing Databases via Delphi

When I first got my copy of Delphi I went through almost exactly the same steps that Eric describes in this chapter. The first step is to use the Delphi wizard to crate a simple database application linked to one of the sample Paradox databases. Having seen what the wizard produces one can carry out much the same task using the underlying controls and facilities. Finally, you can start using ODBC to access data in a Microsoft Access database.

The Delphi Database Form expert makes things look incredibly simple. Unfortunately, life is not like that and when you start having to get to grips with ODBC, tedious little problems tend to cause considerable angst. At least having a worked example in front of you makes the initial learning phase easier. For example, although I was familiar with adding an ODBC data source, it took me some time to work out how to use the Borland Data Engine (BDE) Configuration Utility. Eric tells you what to do in a step by step example but does not tell you why the steps are necessary. I still find the BDE Configuration a bit of a mystery, but so far the steps Eric explains are all that I have needed. However, I have not actually implemented an ODBC application in Delphi. When I do I feel sure that I will have to spend quite some time to really get to grips with the many powerful facilities which appear to be available.

The Delphi Forms to APL Translator

Having had my gripe about the lack of the translator function let’s get back to explaining what Chapter 1 is all about.

Eric feels that the forms designers in both Dyalog APL and APL+Win are a pale shadow in comparison with the Delphi forms designer. He therefore likes to initially design his forms using Delphi and then translate the design into APL. I am not entirely sure that Dyalog APL users would agree with Eric on this score. The wdesign workspace, version 4.2 (Beta), is a big improvement on the old versions. However, APL+Win users would probably agree with Eric, as the ]wed facilities in APL+Win are really rather thin. In either case I really cannot see Causeway enthusiasts bothering as the facilities are reasonable and there are considerable advantages in the integrated environment.

A brief description is in order as you might find it worthwhile considering using the translator that Uniware offer, or even consider building one of your own.

Delphi stores the information about a form and its controls in a .dfm file. You can view the contents of the file in a Delphi edit window, but not, unfortunately in an ordinary text editor as you can with VB. I created a very small example to give you an idea of what is going on. Of course, not having the ParseDFM function I have had to type in the supposed APL output by hand, so please forgive any typing errors.

The following is the display of the .dfm file for a Delphi form with just 4 controls:

object frmTest: TfrmTest
  Left = 200
  Top = 108
  Width = 207
  Height = 110
  Caption = 'Test'
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  PixelsPerInch = 96
  TextHeight = 13
  object lblDesc: TLabel
    Left = 8
    Top = 16
    Width = 53
    Height = 13
    Caption = 'Description'
  end
  object txtDesc: TEdit
    Left = 72
    Top = 16
    Width = 113
    Height = 21
    TabOrder = 0
  end
  object cmdOK: TButton
    Left = 48
    Top = 48
    Width = 49
    Height = 25
    Caption = '&OK'
    TabOrder = 1
  end
  object cmdClose: TButton
    Left = 112
    Top = 48
    Width = 49
    Height = 25
    Caption = '&Close'
    TabOrder = 2
  end
end

All the controls are located on the form and there are no controls within controls. Normally there would be many controls within controls, for example, a File menu bar control will usually contain New, Open and Exit menu controls.

The whole of the displayed .dfm text is copied to the clipboard. Running the ParseDFM function in Dyalog APL/W would produce something like the following:

[0]   R←(FORM)Test A;B;C;D;E;F;G;H;I;J;K;L;FNT_1;⎕IO;⎕ML
[1]
[2]   ⎕IO←1 ⋄ ⎕ML←2
[3]   ⍎(0=⎕NC'FORM')/'FORM←''#.FRM_form'''
[4]   FORM←'#.',(2×'#.'≡2↑FORM)↓FORM←FORM~' '
[5]   'FNT_1'⎕WC'Font'('Size' 15)('PName' 'MS Sans Serif')
[6]         ⍝ →(⎕nc FORM)⍴z
[7]   (FORM)⎕WC'Form'('Coord' 'Pixel')('Accelerator' 27 0)
       ('Event' 1001 1)('3D' 'Dialog')('Border' 2)('Posn' 108 200)
       ('Size' 110 207) ('Caption' 'Test')('Font' 'FNT_1')
[8]   (FORM,'.lblDesc')⎕WC'Label'('Posn' 16 8)('Size' 13 53)
       ('Caption' 'Description')
[9]   (FORM,'.txtDesc')⎕WC'Edit'('Posn' 16 72)('Size' 21 113)
[10]  (FORM,'.cmdOK')⎕WC'Button'('Posn' 48 48)('Size' 25 49)
       ('Caption' '&OK')
[11]  (FORM,'.cmdClose')⎕WC'Button'('Posn' 48 112)
       'Size' 25 49)('Caption' '&Close')
[12]  ⍎FORM,'.FORM←FORM'
[13]  ⎕SE.encaps
[14] z:
[15]  FORM ⎕WS'Visible' 1
[16]        ⍝ ⎕nq(FORM,'.___')'GotFocus'
[17]  R←3⊃(⎕DQ FORM),3⍴¯32768
[18]  →0
[19]
[20]  ∇
[21]  A←Close                 ⍝ ⍎FORM
[22]  ⎕NQ FORM 1001 ¯32768
[23]
[24]  ∇
[25]  Select;A                ⍝ ⍎FORM
[26]  ⎕NQ FORM 1001 ¯32768
[27]
[28]  ∇
[29]  Select A;B;C;D;E;F;G;H;I;J;K;L ⍝ ⍎FORM,'.cmdOK'
[30]  ⎕←A
[31]
[32]  ∇
[33]  Select A;B;C;D;E;F;G;H;I;J;K;L ⍝ ⍎FORM,'.cmdClose'
[34]  ⎕←A

The ⎕SE.encaps function, on line [13], is a utility which fixes the dummy callback functions which are defined after the →0 on line [18]. The code for ⎕SE.encaps function is listed as a footnote in the chapter on Resource files, but does not appear to be included on the companion diskette.

In APL+Win ParseDFM would produce:

∇ A Test B;⎕wself
[1]
[2]   :select B
[3]   :case ''
[4]       'Test'⎕wi 'Delete'
[5]       ⎕wself←'Test'⎕wi'New' 'Form' 'Close'
[6]           ⎕wi 'scale' 5
[7]           ⎕wi 'border' 3 16 32
[8]           ⎕wi 'where'(PixelstoLC 108 200 110 207)
[9]           ⎕wi 'caption' 'Test'
[10]
[11]      ⎕wself←'Test.lblDesc'⎕wi 'New' 'Label'
[12]          ⎕wi 'where' 16 8 13 53
[13]          ⎕wi 'caption' 'Description'
[14]
[15]      ⎕wself←'Test.txtDesc'⎕wi 'New' 'Edit'
[16]          ⎕wi 'where' 16 72 21 113
[17]
[18]      ⎕wself←'Test.cmdOK'⎕wi 'New' 'Button'
[19]          ⎕wi 'onClick' 'Test''cmdOK'''
[20]          ⎕wi 'where' 48 48 25 49
[21]          ⎕wi 'caption' '&OK'
[22]
[23]      ⎕wself←'Test.cmdClose'⎕wi 'New' 'Button'
[24]          ⎕wi 'onClick' 'Test''cmdClose'''
[25]          ⎕wi 'where' 48 112 25 49
[26]          ⎕wi 'caption' '&Close'
[27]
[28]      0 0⍴'Test'⎕wi'Wait'
[29]
[30]  :case 'cmdOK'
[31]
[32]  :case 'cmdClose'
[33]
[34]  :end

I could not find the PixelstoLC function on the companion diskette, but eventually realised that it converts pixels to characters using:

R←A÷(⍴A)⍴2⍴'#'⎕wi'units'.

The onClick events were not properly defined in the book so I have substituted the sort of thing that should have been produced by the ParseDFM function.

From the above it should be easy to see how the Delphi definitions have been converted to APL. Writing your own translator would be tedious but not, in my opinion, very difficult. The main question is whether you really feel that it is worthwhile.

Conclusion

At $90 this is an expensive little book. It is fractionally bigger than Vector and has 196 pages. There are a large number of screen pictures and the examples are quite repetitive, so the actual content is relatively low.

I cannot make up my mind if I would have bought this book at a UK cost of something like £60. I have certainly enjoyed working through the examples and have learned a fair amount. If I was writing a large APL application and was under some time pressure then I would definitely buy the book as it would save some 2 to 3 days’ work. However, I am not currently writing an APL application and am not under any time pressure so the cost seems to be just too much.

Another way to justify a high price for a book is if it is going to be used as a reference manual. The examples are so very simple that having worked though them once there would be little reason to go back and carry out any further study. On the other hand, if Delphi was only used very occasionally then you might want to use the book as a reference manual to refresh you memory on how to program in Delphi.

Thanks

Many thanks to Dyadic Systems Ltd and Bloomsbury Software Ltd who kindly let me have review copies of Dyalog APL/W 8 and APL+Win to help in writing this review.