Ask the MultiValued Visual
Basic Expert - #17

(as published in Spectrum
magazine May/June 1999)

Client Link OCX for mvBASE

Dear Andrew:

We are a large site running mvBASE and
developing VB front-ends for our applications. We acquired the
Client Link OCX from GA, expecting to get VB/mvBASE connectivity,
but we cant make head or tail of the documentation or what
its supposed to do. Do you have any experience with this
tool? - Paul S., Wisconsin

That whirring you hear in the background is the
electric can-opener cracking open 48 ounces of Americas
finest worms. Ill be honest with you: 2 years after the OCX
was released, GAs own support staff still used to recommend
the competing WinLink for VB/mvBASE connectivity. Unfortunately,
if you have a lot of users, the per seat license costs of WinLink
can be prohibitive, so lets focus on the Client Link OCX
for the moment.

An OCX is an extension to Windows programming
languages such as Visual Basic. The Client Link OCX was written
by Dave Holle of GA  a well-respected, long-time MultiValue
guru. Unfortunately for you, in the interests of making it a very
open tool, he designed it to operate at a very low
level. If you can picture the other vendors object-based
connectivity libraries as different brands of automobiles, then
the OCX would be an unattached 4-cylinder engine: flexible and
powerful, but dont plan on driving it off the lot. In order
to use it, you have to do extensive generic communications
programming, both for your Visual Basic projects and on
the mvBASE host. Furthermore, if you can follow the extremely
limited documentation (all of 13 pages) and figure out what it
does, you should be writing this column, not reading it.
To prove my point, Ill attempt to give you the benefit of
my experience with the OCX, including several important
discoveries.

Let me begin with an overview of how it works,
hopefully in terms that real people can understand  a
clarity that the 13-page reference sorely lacks (in my humble
opinion). To use the OCX, you must turn on the Client Link custom
control for your VB project, and add an instance of it to a VB
form. When you invoke its Connect method, the OCX connects just
like a user would: a port is taken up for the session (in the
same way that the mvBASE Virtual Terminal (emulator) works). Note
that a connection does not mean a login (see below). Once a
session connection has been established, you have a choice of two
channels: the Data channel and the Command channel.

The Data Channel

To send a string from VB to the host using the
Data channel, you invoke the SendData method of your Client Link
control, with the string passed as an argument. The string will
be received on the port just as if a user had typed it. In other
words, it can be used to login, submit TCL commands, or satisfy
INPUT statements.

Any string that is output on the port by the
host will trigger a DataReceived event for your Client Link
control, so to get a string back from the host into VB, you just
have to PRINT it. We now have two-way communication. Dont
forget that your VB application has to login first, with enough
smarts to deal with users that leave ports logged in to your
menus and other programs, etc.

Note that you can also send a Break, using the
SendBreak method, but in my experience, once you send one, the
DataReceived event stops being triggered, no matter how much data
is being output (GA could never tell me why). I also believe that
putting pauses at certain points in your VB code (using DoEvents)
can possibly have the same effect. Furthermore, attempting to
track these conversations in VBs debug stepping mode almost
never works  its a bit like quantum physics: your
observations alter (or in this case preclude) the phenomenon. Use
Debug.Print instead.

Now that you can get data going back and forth,
you have to write a host BASIC program that can respond to some
kind of coded requests that you invent in order to get the
program to do work for the VB application (such as reading and
writing items). We have not yet discussed the Command channel; in
fact, I have spoken to programmers who claim that the Data
channel is all you really need. They may be right, but keep its
limitations in mind. INPUT statements do not take kindly to being
blasted with a 32K item to write to your mvBASE file, and the
manner in which output is or may be distributed across one or
more DataReceived events is not documented anywhere. In other
words, strings going to the host may have to be in smaller
packets, and strings coming back may be broken into packets in
ways that are unpredictable and out of your control.

The Command Channel

Mr. Holle decided to introduce some of the
asynchronous concepts of Windows messaging and event-driven
programming into the OCX, using a separate Command channel. To
send a string from VB to the host using the Command channel, you
invoke the SendMessage method of your Client Link control. When
the mvBASE port receives one of these messages, it pushes
whatever is currently running onto an execution stack and runs a
completely new execution thread. This new thread will run
whatever PROC or program is specified by the "@MESSAGE"
item in the current accounts MD. In other words, the
current port activity is put on hold and the port acts as if the
user had submitted the command "@MESSAGE" at TCL. The
idea is that a host program will be started that can use the
GETMESSAGE BASIC function to retrieve the contents of your VB
message, and process requests therein.

To get a string back from the host into VB
using the Command channel, you use the UMESSAGE command in your
host program. Such messages from the host will trigger a
MessageReceived event for your Client Link control. Again, there
are pitfalls that I had to discover for myself, after much
frustration: Dont CHAIN "OFF" from your @MESSAGE
program  you are at a secondary execution level and mvBASE
doesnt clean up the stack properly. If this happens, that
port will not accept anymore sent messages until the whole server
is rebooted (in my experience). (I have also heard reports that
typing END from debug at that secondary level causes similar
grief). Furthermore, the UMESSAGE/MesssageReceived combination
seems to have trouble with messages larger then about 10,000
characters.

Another drawback of the Command channel is that
every message sent from VB will be processed by a separate
@MESSAGE execution. In other words, you cant just send one
message to open a host file and then another one to read an item,
because the open file variable will not exist in the second
execution. The way around this is to use a named COMMON block.
Variables in a named COMMON block retain their value for the
duration of the login session (i.e. across @MESSAGE executions).

More Quirks and Caveats

If you are still keen on developing with the
Client Link OCX, here are a few other issues to be aware of:

When you initially invoke the Connect method to
start a session, a MessageReceived event will be immediately
triggered with a null message. This is not documented anywhere
and might throw your logic if you arent ready for it.

Something (either the SendData method or the
UMESSAGE host function) appears to also generate a CRLF on the
port output (which I seem to recall also triggers a DataReceived
event). Ignoring empty data strings in the DataReceived event is
a real necessity.

One of the subtlest catches about using events
to return strings to Visual Basic is that you now have to match
up results to requests and keep your world in synch. After your
VB application makes a request for data from the host, and it is
waiting for a result, it will still respond to other events and
user actions unless specifically programmed not to. For example,
your order entry screen might have a command button that reads a
customer record, using a text box key. While the host is
processing that request, the user might change the order key to
an existing order and read that one. What will your VB program do
with the original customer record when the host finally sends it?

Of course, this asynchronous behavior might
also be considered a powerful feature, if you do the work to take
advantage of its potential benefits. For example, let us presume
that your user enters a client number. You might want to pause
the application until the client name is echoed back, but why
shouldn't the user proceed on while other information is also
looked up? One way to do this is to attach some kind of
identifier to the request for data. Then have your host program
include the identifier with the response. Your MessageReceived
event code would then process the response (in this case: other
client data), based on the identifier. You can maintain control
of the application by only enabling certain fields or buttons
when certain information has been returned. On the other hand, if
you want to force synchronous behavior for some tasks (such as a
lot of look ups), you can prevent the user from pushing ahead (Screen.ActiveForm.Enabled = False and
Screen.MousePointer = vbHourglass) until
the application is ready.

MVB Library

Regular readers of this column will be familiar
with my concept of a function library that gives VB full host
BASIC functionality using a common, non-proprietary syntax,
regardless of the transport medium. After several fistfuls of
hair, I managed to complete a reasonably reliable version of the MVB library that uses the Client Clink OCX. Here are some of my
'secrets':

Since the link will only work when the port is
logged on, I use the Data channel and some sophisticated decision
trees to ensure that the port is logged on to the appropriate
account and able to run my modified MVB host software. I do not
use the SendMessage/GETMESSAGE combination for two reasons: 1) I
found the pushing/popping of the execution stack to have
unreliable effects on my ports; and 2) I only want the host
program to run once per session (in order to hold open file and
select lists), not once per message. (I could have used Named
COMMON, as mentioned above, but elected not to.)

All of my requests from the VB side therefore
use the SendData/INPUT combination, with packeting for large data
strings. My real trick is to use the UMESSAGE/MessageReceived
combination for returning results to VB from the host, thereby
eliminating the unpredictable way that strings are broken up for
DataReceived events. In other words, requests are issued using
the Data channel, responses are returned using the Command
channel.

This column is not intended to be an
advertisement, but given what has been said to this point, you
should be aware of your options. The MVB library is available (as
source) from Caduceus Consulting at a modest cost. There are also
three other alternatives: GA recently announced a new product
called "Transfers"(?) from Coyote Systems, that uses
the OCX and purports to give out-of-the-box functionality; the
WinLink family of products from Via Systems give you similar functionality for mvBASE and other
MultiValue flavors; and Liberty supposedly has an API that works
with mvBASE (they dont seem to push it much and I have
never seen it). In short, I dont recommend attempting to
reproduce a lot of the above or to start your own use of the
Client Link OCX from scratch  not unless you are a real
sucker for punishment.

Dont try this at home, kids. These are
trained professionals.

[P.S. The client who sent this is now quite
successfully using the OCX (courtesy of the MVB library). A happy
ending!]