Ask the MultiValued Visual Basic Expert - #17

(as published in Spectrum magazine May/June 1999)

To email your questions to "Ask the MultiValued VB Expert", click here.
Copyright 1996-99 Caduceus Consulting. All rights reserved.

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!]

To email your questions to "Ask the MultiValued VB Expert", click here.
Copyright 1996-99 Caduceus Consulting. All rights reserved.
Added: August 18, 1999.

Return to Caduceus Consulting Home Page

Copyright 2006 intellact
Last modified: Thursday May 25, 2006