Generic IDispatch Proxy Classes for Active Scripting (Delphi 2010+)

Playing around the Delphi 2010 trial, I came up with some generic IDispatch proxy classes, one each for objects, metaclasses, records, dynamic arrays, sets and set types, along with an adapter class for enumerators that exposes them as IEnumVARIANT objects. In use, the vast majority of proxies will be created on the fly as polled for by the scripting host — basically, you will only need to explicitly instantiate the proxies for top-level objects.

Now, you may be wondering what on earth is the point of writing generic IDispatch proxy classes. Well, the aim was to make adding Active Scripting support to an application very easy, and thus, enable end users to write scripts (either in VBScript or JScript) that manipulate native Delphi objects. Nonetheless, there is, of course, more to adding Active Scripting support to an application than simply exposing certain objects — for in short, you need to expose them to something, namely a scripting host. For this, Microsoft defined some interfaces that an application needs to implement; these can then either be implemented yourself or by an instance of the MS Script Control. At least for ‘proof of concept’ purposes, the latter option is a much easier proposition.

Putting everything together, I’ve then created two demo apps. The first follows the default of exposing everything that can be exposed, and the second, more realistic demo takes the form of a scriptable text editor (‘TextScript’) that aims to expose only what is appropriate, exposing script-specific classes if necessary. The most significant issue here is that VBScript provides no equivalent to Delphi’s try/finally syntax; because of this, the second demo filters out most constructors and all destructors, along with other things that should really be ‘internal’ to an app and not exposed for scripting. Nonetheless, it still allows writing and executing VBScript macros like the following:

Option Explicit

Const DlgCaption = "Create New Letter Wizard"
Const NeedClosePrompt = "The current document needs to be closed before running the macro. Save the document now? (Clicking No will abandon any changes, clicking Cancel will cancel the macro.)"

Const MyName = "Chris Rolliston"

Const fsBold = 0
Const taLeftJustify  = 0
Const taCenter       = 2
Const taRightJustify = 1

If not Editor.Modified Then
  DoIt
Else
  Select Case MsgBox(NeedClosePrompt, vbYesNoCancel + vbQuestion, DlgCaption)
    Case vbNo
      DoIt
    Case vbYes
      If Document.Save Then DoIt
  End Select
End If

Sub DoIt
  ' Forcibly close the current document; since this is an SDI app,
  ' closing one document starts another
  Document.Close True
  ' Request the subject line and recipient from the user
  Dim SubjectLine, Recipient
  SubjectLine = InputBox("Enter the subject line (press Cancel " + _
    "to not have a subject line):", DlgCaption, "Re: ")
  Recipient = InputBox("Enter the recipient:", DlgCaption, "Sir/Madam")
  If Len(Recipient) = 0 Then
    MsgBox "Cancelled letter creation", vbInformation, DlgCaption
    Exit Sub
  End If
  ' Add the address and date info to the document, right aligned.
  Editor.Paragraph.Alignment = taRightJustify
  Editor.Lines.Add "2 Rtti Road"
  Editor.Lines.Add "Delphichester"
  Editor.Lines.Add "Embarcashire"
  Editor.Lines.Add "RS20 1OT"
  Editor.Lines.Add ""
  Editor.Lines.Add FormatDateTime(Date)
  ' If the user provided one, add the subject line, centred and bold
  Editor.Paragraph.Alignment = taCenter
  If Len(SubjectLine) <> 0 Then
    Set Editor.SelAttributes.Style = TFontStyles.Create(fsBold)
    Editor.Lines.Add SubjectLine
  End If
  ' Write the outline of the message body
  Editor.Paragraph.Alignment = taLeftJustify
  Editor.Lines.Add "Dear " & Recipient
  Editor.Lines.Add ""
  Dim SavedSelPos, Valediction
  SavedSelPos = Editor.SelStart 'we'll be moving the caret to here at the end
  Editor.Lines.Add ""
  Editor.Lines.Add ""
  If InStr(Recipient, "Sir") Or InStr(Recipient, "Madam") Then
    Valediction = "Yours faithfully"
  Else
    Valediction = "Yours sincerely"
  End If
  Editor.Lines.Add Valediction
  Editor.SelText = MyName
  Editor.SelStart = SavedSelPos
  ' Give the editor the focus
  Editor.SetFocus
End Sub

Because the code has taken a bit of work on my part, I’ve put a licence on it; as with most of the stuff I’ve put up though, it’s only an MPL one. Anyhow, you can download it from here.

2 thoughts on “Generic IDispatch Proxy Classes for Active Scripting (Delphi 2010+)

  1. Hi Chris,
    thank you for your work.
    Generic IDispatch is something that was missing for very long time.
    Now I event think of reborning my old project on this native engine and getting rid of buggy third-party scripters. Definately, I will give it a try.

    Just one question- is it possible to set up event handlers from script?

    • Alex —

      The short answer is that no, I didn’t write a proxy class for event handlers. The longer answer, though, is that while such a class might nevertheless be written, *how* it would be written depends on what you want to achieve exactly.

      In particular, compare event handling in Delphi to event handling in ‘classic’ Visual Basic (including VBA): where the former is ‘freeform’ in that an event handler can be named anything you wish, and be assigned — and un-assigned — at any point, in VB/VBA, an event handler has to have a fixed name and can only be assigned at design time.

      Now if you wanted to achieve Delphi-style event handling in script, you would immediately encounter lifetime issues – i.e., when the script object is reset, you would have to go round unassigning any events that had been assigned to in script. More basically, what should happen once a script has been run – should the code still stay in memory to preserve any event handlers that have been set, or should both script object and event handlers be reset?

      On the other hand, if VBA-style event handling was all you wanted, the range of settable events would be predefined; indeed, it might even be implemented in terms of one script per settable event, not needing any generic proxy be written at all. Also, think of Word or Excel: the exposed event model is an abstraction beyond the actual ‘events’ of the internal application. Moreover, Word or Excel doesn’t keep any executable VBA code in memory: rather, such code is only in memory while it is being run.

Comments are closed.