Chapters

  1. Introduction
  2. Structure
  3. Packaging
  4. Logging
  5. Configuration
  6. Debugging EXEs
  7. Handling errors
  8. Testing
  9. Documentation
  10. Make
  11. Providing help
  12. Scheduled Tasks
  13. Windows Services
  14. Windows Event Log
  15. Windows Registry
  16. Creating SetUp.exe
  17. Regular Expressions
  18. Acre
  19. GUI
  20. Git

Appendices

  1. Windows environment vars
  2. User commands
  3. aplcores & WS integrity
  4. Development environment
  5. Special characters

Misc

Users expect applications to provide help in one way or another. One option is to provide the help as a hypertext system. Under Windows, CHM files are the standard way to provide such help. There are powerful applications available that can assist you in providing help; HelpAndManual [1] is just an example.

However, we take a different approach here: rather than using any third-party software we use Markdown2Help from the APL_cation [2] project. That allows us to create a help system that:

This is the simplest way to create a Help system, and it allows you to run the Help system from within your application in order to view either its start page or a particular page as well as viewing the Help system without running your application at all.

While CHM files are Windows specific, Markdown2Help allows you to export a Help system as a web page that can be displayed with any modern browser. That makes it OS-independent. We’ll discuss later how to do this.

It’s time to save a copy of Z:\code\v10 as Z:\code\v11.

To use Markdown2Help you need to download it from http://download.aplwiki.com/. We suggest creating a folder Markdown2Help within the folder Z:\code\APLTree. Copy into Z:\code\APLTree\Markdown2Help the contents of the ZIP you’ve just downloaded:

Download target

Within that folder you will find a workspace Markdown2Help (from which we are going to copy the module) and a folder help.

This folder contains in turn a subfolder files (which contains Markdown2Help’s own Help system) and the file ViewHelp.exe. That EXE is the external viewer for viewing your Help system independently from your application.

Double-click ViewHelp.exe in order to see Markdown2Help2’s own Help system:

Markdown2Help's Help

By default, ViewHelp.exe expects to find a folder files as a sibling of itself, and it assumes this folder contains a Help system.

Specify help folder and help page

You can change the folder ViewHelper.exe expects to host the Help system by specifying a command-line parameter helpFolder:

ViewHelp.exe -helpfolder=C:\Foo\Help

You can also tell ViewHelper.exe to put a particular help page on display rather than the default page:

ViewHelp.exe -page=Sub.Foo

However, all these details are discussed in Markdown2Help’s own Help system.

Markdown2Help is an ordinary (non-scripted) namespace. We therefore need to copy it from its workspace. We also need the script MarkAPL, which is used to convert the help pages from Markdown to HTML. You know by now how to download scripts from the APLTree library. Modify MyApp.dyapp so that it loads the module MarkAPL and also copies Markdown2Help:

...
Load ..\AplTree\Execute
Load ..\AplTree\MarkAPL
Run 'Markdown2Help' #.⎕CY '..\apltree\Markdown2Help\Markdown2Help.dws'
Load Tests
...

Double-click the DYAPP to get started.

Markdown2Help comes with a function CreateStub that creates a new Help system for us. We need an unused name for it: the obvious candidate is MyHelp.

We want the Help system managed by SALT, for which we need a folder for all the Help files. For that we call CreateParms and then specify the folder in the parameter saltFolder:

parms←#.Markdown2Help.CreateParms ⍬
parms.saltFolder←'Z:\code\v11\MyHelp'
parms.folderName←'Z:\code\v11\Help\Files'
parms #.Markdown2Help.CreateStub '#.MyHelp'

CreateStub will create some pages and a node (or folder) for us; here’s what you should see:

Download target

Notes:

In the workspace all nodes (in our case MyHelp and Sub) are ordinary namespaces, while the pages are variables. You can check with the Workspace Explorer:

The help system in the Workspace Explorer

This is why the names of nodes and pages must be valid APL names. Those names appear in the Help tree as topics by default, but we can of course improve on that. We’ll come back to this soon.

When you right-click on a page like Copyright and then select Edit help page from the context menu (pressing <Ctrl+Enter> will do the same) the APL editor opens and shows something similar to this:

A help page in the editor

This is the source of the help page in Markdown.

Notes:

Make some changes, for example add another paragraph Go to →[Overview], and then press Esc. Markdown2Help takes your changes, converts the Markdown to HTML and shows you the changed page.

This gives you an idea of how easy it actually is to change help pages. Adding, renaming and deleting help pages – and nodes – can be achieved via the context menu.

Note also that →[Overview] is a link. For the link to work Overview must be the name of an existing page. If the title of the page differs from the name, the title will appear as the link text in the help page.

Watch out Read Markdown2Help’s own help file before you start using Markdown2Help in earnest. Some Markdown features are not supported by the Help system, and internal links are implemented in a simplified way.

Note that the Copyright page comes first. That’s because by default the pages are ordered alphabetically. You can change this with a right-click on either the Copyright or the Overview page and then selecting Manage ∆TopicProperties.

After confirming this is really what you want to do you will see something like this:

 ∆TopicProperties←{
⍝ This function is needed by the Markdown2Help system.
⍝ You can edit this function from the Markdown2Help GUI via the context menu.
⍝ *** NOTE:
⍝     Make only changes to this function that affect the explicit result.
⍝     Any other changes will eventually disappear because these functions are rebuilt
⍝     under program control from their explicit result under certain circumstances.
⍝        This is also the reason why you should use the `active` flag to hide a topic
⍝     temporarily because although just putting a `⍝` symbol in front of its line
⍝     seems to have the same effect, in the long run that's not true because the
⍝     commented line will disappear in the event of a rebuild.
⍝ ----------------------------------
⍝ r gets a table with these columns:
⍝ [;0] namespace or function name.
⍝ [;1] caption in the tree view. If empty the namespace/fns name is taken.
⍝ [;2] active flag.
⍝ [;3] developmentOnly flag; 1=the corresponding node does not show in user mode.
     r←0 4⍴''
     r⍪←'Copyright' '' 1 0
     r⍪←'Overview' '' 1 0
     r⍪←'Sub' '' 1 0
     r
}

We recommend reading the comments in this function.

You can specify a different sequence of the pages simply by changing the sequence in which the pages are added to r. Here we swap the position of Copyright and Overview:

 ∆TopicProperties←{
     ...
     r←0 4⍴''
     r⍪←'Overview' 'Miller''s overview' 1 0
     r⍪←'Copyright' '' 1 0
     r⍪←'Sub' '' 1 0
     r
 }

We have also changed the title of the Overview page to Miller’s overview. That’s how you can specify an alternative title to the name of the page.

After fixing the function, the Help system is recompiled automatically; and our changes become visible immediately:

The changed help system

What “compiling the help system” actually means is discussed soon.

The context menu has many commands. The first three commands are always available. The other commands are useful for a developer (or shall we say Help system author?) and are available only when the Help system is running in a development version of Dyalog.

The context menu

As a developer you will have no problem mastering these commands.

Compiling the help system converts

into a single component file (DCF) containing the HTML generated from the Markdown, plus some more pieces of information.

It’s more than just converting Markdown to HTML. For example, the words of all the pages are extracted, ‘dead’ words like and, then, it, etc. are removed (because searching for them does not make too much sense) and the index, together with pointers to the pages they appear on, are saved in a component.

This allows Markdown2Help to provide a very fast Search function. The list is actually saved in two forms, ‘as is’ and with all words lowercased to speed up any case-insensitive search operations.

Without specifying a particular folder, Markdown2Help would create a temporary folder and compile into that folder. It is better to define a permanent location, which avoids having the Help system compile the Markdown into HTML whenever it is called.

Such a permanent location is also the precondition for using the Help system with the external viewer, necessary if your Help system tells how to install your application.

For converting the Markdown to HTML, Markdown2Help needs the MarkAPL class, but once the Help system has been compiled this class is no longer needed. The final version of your application does not need MarkAPL. As MarkAPL comprises roughly 3,000 lines of code, this is good news.

Besides editing a variable with a double-click in the Workspace Explorer, you could also edit it from the session with )ED. Our advice: don't!

The reason is simple: when you change a Help system via the context menu then other important steps are performed. An example is when you have a ∆TopicProperties function in a particular node and you want to add a new help page to that node.

You have to right-click on a page and select the Inject new help page (stub) command from the context menu. You will then be prompted for a valid name and finally the new help page is injected after the page you have clicked at.

But there is more to it than just that: the page is also added to the ∆TopicProperties function. That’s one reason why you should perform all changes via the context menu rather than manipulating the Help system directly.

Maybe even more important: Markdown2Help also executes the necessary steps in order to keep the files and folders in saltFolder in sync with the Help system and automatically recompiles the Help system for you.

The only exception is when you change your mind about the structure of a Help system. If that involves moving around namespaces or plenty of pages between namespaces then it is indeed better to do it in the Workspace Explorer and, when you are done, to check all the ∆TopicProperties functions within your Help system and finally recompile the Help system; unless somebody implements drag-and-drop for the TreeView of the Help system one day…

However, if you do that, you must ensure the Help system is saved properly. That means that you have to invoke the SaveHelpSystemWithSalt method yourself. You also need to call the Markdown2Help.CompileHelpFileInto method to compile the Help system from the source. Refer to Markdown2Help’s own Help system for details.

If the Help system is running under a development version of Dyalog, you will see a Developers menu on the right side of the menubar. This offers commands that support you in keeping your Help system healthy. We discuss just the most important ones:

Particularly useful when you use non-default CSS and there is a problem with it: all modern browsers offer excellent tools for investigating CSS, supporting you when hunting bugs or trying to understand unexpected behaviour.

This command creates an HTML document from all the help pages and writes the HTML to a temporary file. The filename is printed to the session.

You can then open that document with your preferred word processor, say Microsoft Word. This will show something like this:

The help system as a single HTML page

This is a great help when it comes to proofreading a document: one can use the review features of the word processor, and also print the document. You are much more likely to spot any problems in a printed copy than on a screen.

Several reports identify broken and ambiguous links, ∆TopicProperties functions, and help pages that do not carry any index entries.

You can export the Help system as a website. For that select Export as HTML from the File menu.

The resulting website does not offer all the features the Windows version comes with, but you can read and print all the pages, you have the tree structure representing the contents, and all the links work. <!– That must do under Linux and macOS for the time being. –>

If you have not copied the contents of code\v11\* from the book’s website then you need to make adjustments to the Help system to keep it in sync with the book. We have just two help pages; a page regarding the main method TxtToCsv:

The changed help system

And a page regarding copyright:

The changed help system

We want to confirm we can call the Help system from within our application. For that we need a new function; its obvious name is ShowHelp.

The function’s vector right argument is the name of the page the Help system should open at; if empty, the first page is shown. It returns an instance of the Help system. The function goes into the MyApp.dyalog script:

:Namespace MyApp
...
∇

∇{r}←ShowHelp pagename;ps
  ps←#.Markdown2Help.CreateParms ⍬
  ps.source←#.MyHelp
  ps.foldername←'Help'
  ps.helpAbout←'MyApp''s help system by John Doe'
  ps.helpCaption←'MyApp Help'
  ps.helpIcon←'file://',##.FilesAndDirs.PWD,'\images\logo.ico'
  ps.helpVersion←'1.0.0'
  ps.helpVersionDate←'YYYY-MM-DD'
  ps.page←pagename
  ps.regPath←'HKCU\Software\MyApp'
  ps.noClose←1
  r←#.Markdown2Help.New ps
∇

∇ r←Public
  r←'StartFromCmdLine' 'TxtToCsv' 'SetLX' 'ShowHelp'
∇

:EndNamespace
Information

A Windows Registry key? The user can mark any help page as a favourite, and this is saved in the Windows Registry. We will discuss the Windows Registry in a later chapter.

This function requires the Help system to be available in the workspace.

Strictly speaking, only the source parameter needs to be specified to get it to work, but best to specify the other parameters too before a client sets eyes on your Help system.

Most of the parameters should explain themselves, but if in doubt you can always start Markdown2Help’s own Help system with #.Markdown2Help.Selfie ⍬ and read the pages under the Parameters node. Here’s what you should see:

The context menu

You can request a list of all parameters with their default values with this statement:

      ⎕←(#.Markdown2Help.CreateParms'').∆List''

Note that CreateParms is one of the few functions in the APLTree library so named that actually requires a right argument. <!– Breaking our rule! –> This right argument may be just an empty vector, but instead it could be a namespace with variables like source or page. In that case CreateParms would inject any missing parameters into that namespace and return it as a result.

Therefore we could rewrite the function ShowHelp:

∇{r}←ShowHelp pagename;ps
  ps←⎕NS ''
  ps.source←#.MyHelp
  ps.foldername←'Help'
  ps.helpAbout←'MyApp''s help system by John Doe'
  ps.helpCaption←'MyApp Help'
  ps.helpIcon←'file://',##.FilesAndDirs.PWD,'\images\logo.ico'
  ps.helpVersion←'1.0.0'
  ps.helpVersionDate←'YYYY-MM-DD'
  ps.page←pagename
  ps.regPath←'HKCU\Software\MyApp'
  ps.noClose←1
  ps←#.Markdown2Help.CreateParms ps
  r←#.Markdown2Help.New ps
∇

This version of ShowHelp would produce exactly the same result.

Now that we have a Help system that is saved in the right place we have to ensure it is loaded when we assemble a workspace with a DYAPP. First we add a function LoadHelp to the DevHelpers class:

:Namespace DevHelpers
...

    ∇{r}←LoadHelp dummy;parms
    parms←#.Markdown2Help.CreateParms ⍬
    parms.saltFolder←#.FilesAndDirs.PWD,'\MyHelp'
    parms.source←'#.MyHelp'
    parms.folderName←#.FilesAndDirs.PWD,'\Help\Files'
    {}#.Markdown2Help.LoadHelpWithSalt parms
    ∇

:EndNamespace

Calling this function will load the Help system from saltFolder into the namespace #.MyHelp in the current workspace. So we need to call this function within MyApp.dyapp:

...
Load DevHelpers
Run DevHelpers.LoadHelp ⍬
Run #.MyApp.SetLX ⍬
...

Now we need to ensure the Make process includes the Help system. First we add the required modules to Make.dyapp:

Target #
Load ..\AplTree\APLTreeUtils
Load ..\AplTree\FilesAndDirs
Load ..\AplTree\HandleError
Load ..\AplTree\IniFiles
Load ..\AplTree\OS
Load ..\AplTree\Logger
Load ..\AplTree\EventCodes
Load ..\APLTree\WinReg
Run 'Markdown2Help' #.⎕CY '..\apltree\Markdown2Help\Markdown2Help.dws'
Load Constants
Load Utilities
Load MyApp
Run #.MyApp.SetLX ⍬

Load Make
Run #.Make.Run 1

Finally we ensure the compiled Help system is copied over together with the standalone Help Viewer:

:Class Make
...
⍝ 5. Creates `MyApp.exe` within `DESTINATION\`
⍝ 6. Copy the Help system into `DESTINATION\Help\files`
⍝ 7. Copy the stand-alone Help viewer into `DESTINATION\Help`
⎕IO←1 ⋄ ⎕ML←1

    DESTINATION←'MyApp'

    ∇ {filename}←Run offFlag;rc;en;more;successFlag;F;U;msg
      :Access Public Shared
      (F U)←##.(FilesAndDirs Utilities)
      (rc en more)←F.RmDir DESTINATION
      U.Assert 0=rc
      successFlag←'Create!'F.CheckPath DESTINATION
      U.Assert successFlag
      (successFlag more)←2↑'images'F.CopyTree DESTINATION,'\images'
      U.Assert successFlag
      (rc more)←'MyApp.ini.template'F.CopyTo DESTINATION,'\MyApp.ini'
      U.Assert 0=rc
      (successFlag more)←2↑'Help\files'F.CopyTree DESTINATION,'\Help\files'
      U.Assert successFlag
      (rc more)←'..\apltree\Markdown2Help\help\ViewHelp.exe'F.CopyTo DESTINATION,'\Help\'
      U.Assert 0=rc
      Export'MyApp.exe'
      filename←DESTINATION,'\MyApp.exe'
      :If offFlag
          ⎕OFF
      :EndIf
    ∇
...
:EndClass