This is another topic that is documented in the SDK (in chapter 15, advanced
programming tools), but as always, what is written is somewhat ambiguous and a
few more examples would not have hurt. After having spent no small amount of
time figuring out what works, I want to record my experiences here.
But first of all: why do I need to execute a Priority command from an external
application? Lately we've been working on raising the level of computerisation
on the factory floor by means of barcode readers. My original concept was that
there would be a computer running Priority with a special, private and
bespoke, screen being displayed, into which barcodes are scanned. The screen
has a post-insert trigger for the lines that does something useful and clever.
The next step came from someone asking why this computer has to be running
Priority? There are both advantages and disadvantages to this: maybe the
disadvantages can be overcome without losing the advantages? So I suggested
writing a program in Delphi that accepts the barcodes, saves them to a file
and then transfers that file to the server where it is read by a procedure
that is activated every half an hour.
An even better solution would be that the computer running a program in Delphi
would accept a barcode and transmit it immediately to Priority,
possibly handling that barcode at the same time. I had an arrangement like
this twenty years ago! It's called Remote Execution, or Rexec.
In a part of the SDK that I had managed to miss was the section that I needed:
the calling computer runs a program called WINRUN on the server that runs
WINACTIV that runs a procedure (this is only one of the possibilities
documented). As I wrote at the beginning, the documentation is ambiguous and I
want to explain it fully here.
First off: TABULA.INI. I found the copy of this file that is stored on the SQL
server (i.e. the copy that is called when Priority is run on the server) -
it's stored in C:\WINDOWS of the server and so is not normally visible.
On our server there is a directory D:\pr_SQL that has subdirectories BIN.95,
system, etc - i.e. Priority; each installation chooses its own directory for
this. This directory is mapped as X: on client computers; on the server I
copied c:\windows\tabula.ini to x:\1\tabula.ini, making this file visible to
all. On the client computer, where my Delphi program will be run, I defined an
environmental variable thus: set TABULAINI=x:\1\tabula.ini. This only has to
be done once on the computer.
I then wrote a procedure in Priority, TEST_UPDCONST. This procedure simply
updates a value in a private table whenever it is run - this way I could see
whether what I was doing was working. Once I had the command debugged, I then investigated how I could pass parameters to the procedure (this obviates the need to transfer files or similar).
Here is the final command that must be
sent to Priority, along with comments.
| Command part |
Description |
Constant |
| X:\BIN.95\WINRUN |
Winrun is the program that is run on the server; it is situated at
X:\BIN.95 on my server. The location should be BIN.95 on the drive where
Priority is located.
|
Y |
""
|
Two double quotation marks. Don't ask me why. |
Y |
tabula 123456
|
The account name and password. It makes sense to have tabula run the
procedure, thus preventing any problem with permissions. Of course, the
password is not 123456.
|
|
x:\system\prep
|
This directory is always used when preparing procedures. |
Y |
demo
|
The name of the company (environment) in which the command will be run
|
|
| X:\BIN.95\WINACTIV.EXE |
This is the name of the program that runs the procedure. It didn't work
without prefacing the name with its location.
|
Y |
| -P |
A procedure is being run |
Y |
| TEST_INSERTNESTBC |
This is the name of the procedure |
|
| -var:BC "A09" |
First variable that is being passed to the procedure, whose name is BC
and whose value is "A09"
|
|
| -var:TS "12/11/21 09:04" |
Second variable that is being passed to the procedure, whose name is TS
and whose value is "12/11/21 09:04" (i.e. the current date and time)
|
|
Put together, the complete command is X:\BIN.95\WINRUN "" tabula 123456 x:\system\prep demo X:\BIN.95\WINACTIV.EXE -P TEST_INSERTNESTBC -var:BC "A09" -var:TS "12/11/21 09:04".
In the above table, there is a column 'constant'; if the value is Y, then the command part should be written as is (obviously substituting the correct directory on the server). The SDK does not mention that the parameters of the command are case sensitive: if one is passing parameters to the procedure being called, then one must write "-var" and not "-VAR" as I originally did. One optional parameter is "err <filename>": this causes error messages from the called procedure to be written in this file; again, it's "err" in lower case, not "ERR".
The SDK also documents how the called procedure should accept its parameters; I thought that this was ambiguous but turns out to be accurate. In the case shown above, the called procedure (TEST_INSERTNESTBC) has two char parameters, BC and TS. Inside the procedure, they are referred to as :EXTERNAL.BC and :EXTERNAL.TS, not :$.BC and :$.TS. My misunderstanding was with reference to 'EXTERNAL'; in chapter 2 of the SDK there is a list of predefined variables, such as :PAR1, :PAR2, :PAR3 and :KEYSTROKES. I've noted in this blog a few undocumented variables to add to this list. I did not notice that the last variable appearing in the list is :EXTERNAL.VARNAME, "used in procedures to refer to variables inputted [sic] as part of the WINRUN command". This is similar to the half-documented variable :PROGPAR.VARNAME that I discussed here.
So now I have a very powerful tool at my disposal. First it will be used in a specific setting (that received its inspiration from another Delphi program that I developed this week that avoided having to access Priority), but I'm sure that in the coming months, this technique will be used more frequently.
It should be noted that this technique is s-l-o-w: it takes about five minutes for the entire process.