DMH.ocx - A Visual Basic DMH Active-X Control


Hume Integration has created an Active-X control for integrating Visual Basic (VB) programs into a distributed system using the Distributed Message Hub (DMH) message system.   The VB DMH control component provides high-level methods for sending and receiving messages, with either send-and-reply synchronous-style interactions, or higher performance asynchronous-style interactions.    Message exchanges are typically directed to application servers such as the Hume Integration Datahub, or the dmh_SQLsrv persistent database interface.  The DMH message system is remarkably easy to use with these DMH Tcl processes because the messages are typically SQL, Tcl, or VFEI text that is directly interpreted by the receiver.   To extend this ease of use to the VB DMH client, methods are provided with the control to convert Tcl list text strings into native VB data types, or to create Tcl list text strings from VB data.  Also, a method is provided to convert SEMATECH VFEI formatted text into Dictionary entries.  (The Dictionary class is the native VB associative array.  VFEI is a self-describing, name-equals-value text format that is used for equipment integration.)  The VB DMH Active-X control fully supports peer-to-peer interactions.

Feature Summary


User Guide


The DMH Active-X control for Visual Basic is provided as a file, DMH.ocx, for the Windows 2000 and Windows NT x86 architecture platform.   The .ocx binary file is provided with a test and demonstration application, TestDMHProj.exe, along with the test application source code, and project file.  The files are distributed in a .zip archive file, DMHocx.zip. The documentation files in the archive are copies of the same files that are incorporated into the main online documentation.

The control is designed to successfully interoperate with any recent version of the Tcl/Tk DMH software running on any platform.

Installation

If you received the DMH software on CDROM, the DMHocx.zip archive is found in the subdirectory LAN_IMAGE. The DMHocx.zip distribution archive needs to be unzipped into a path of your choosing; for example, c:\usr\local. The archive includes the subdirectory DMHocx, so the complete pathname to the DMH.ocx file becomes your_path\DMHocx\DMH.ocx. Only the DMH.ocx file is needed for a runtime deployment. For runtime-only machines, you may choose to copy just this file to the directory where your VB application .exe files and .dll files are installed.

As with any Active-X component, the ocx file needs to be registered on a computer system before being used.   Registration is a one-time activity.  The installation path should be chosen carefully since you need to unregister and re-register the .ocx if you move it.  Registration logic is also built into programs that are built using VB6.  For example, if you run the test and demonstration program that comes with the control, TESTDMHProj.exe, the DMH.ocx control gets registered using its current file system location.  The registration can also be performed at the command line by executing

    regsvr32 pathname\DMH.ocx

where pathname is your pathname to the file.   The VB6 Development Environment can also register a component - refer to its documentation.

At runtime, the control uses the VB6 runtime DLL,  the Microsoft Scripting Runtime, and the Microsoft Winsock Control 6.0 (%SystemRoot%\System32\MSWINSCK.OCX).  These DLLs/controls may need to be copied  from a development machine to any runtime machine where VB6 has not been installed.  If they are copied, they also need to be registered.

Development

The control is used from Visual Basic Version 6 as follows.  Use the menu item Project/Components... to bring up a list of available components.  Check the "Hume Integration DMH VB Active-X Control" checkbox to make the control available to your project.  Press the Ok button.

Now when you visually edit your main form, you should see the "DMH" control as a selectable component on your toolbar.

Add one instance of the DMH control to your form by double-clicking on the "DMH" component icon.  VB gives your instance the name DMH1 but you may wish to assign a name of your choosing.  We will use the name "dmh" for example.  You only need one instance of the control - it should be shared among all your forms, as discussed later in this section.  The control will be invisible at runtime.  Place it on your form as you see fit.

As a developer, per the licensing terms, you are required to have the Tcl/Tk DMH software installed on your development system.   One reason for this requirement is to insure that you have the online documentation for the DMH system.  A second major reason is so that you can run your own DMH server process to test and debug against without affecting a factory production system.

If you are not familar with the DMH message system, you may want to read the mbx document which is usually installed at \usr\local\htm83\mann\mbx.html.  Your VB application acts a DMH message system client, and attaches to a running DMH server.  Once you are connected to the DMH system, you can exchange messages with other attached processes.

To get started, you only need to know the hostname where the DMH server is running, and the DMH Groupname that has been assigned to the server.   If you click on the "Programs"/"Tcl 8.3 - Tk 8.3 - DMH"/"Datahub" program item, you will start a DMH server running on your host, with the default DMH groupname of "mbx".

The usual application design, and the one that provides the best performance,  is to connect to the DMH server during initialization, and to use this connection during the life of the application.  The Init( ) method is used to connect to the server.  The server's hostname and groupname can be provided as arguments to the Init( ) method, or set as property values during development or runtime.  One idea is to set the production system hostname and groupname at development time as property values, and then at runtime check to see if environment variables of your choice are pointing to an alternate DMH server.  You can call the Init( ) method from your Form_Load subroutine.

Error Handling

A long running program such as a user interface or automation application needs to handle intermittant errors with network outages or server shutdowns.  There are two styles of handling errors; you can mix both styles in your application as you see fit.

The first style of error handling is to avoid them by disabling buttons and window controls that make use of DMH features when there is not a server connection.  You write the statements that enable your buttons in the control "Connected" event handler, and you write statements that disable the buttons in your "Disconnected" event handler.  During initialization, before you have a connection, call the Disconnected event handler yourself to disable the buttons.  Also during initialization, call the Init( ) method.  When the Init( ) succeeds, a "Connected" event occurs and your buttons become enabled.  This technique requires less coding than the method described next.  Your code may look something like this:

Private Sub dmh_Connected()
    TraceAppend ("Connected Event")
    butInit.Caption = "Disconnect"
    'enable buttons that require a connection
    butDoXact.Enabled = True
    butSend.Enabled = True
    'etcetera
End Sub

Private Sub dmh_Disconnected()
    TraceAppend ("Disconnected Event")
    butInit.Caption = "Initialize"
    'disable buttons that need dmh to avoid errors
    butDoXact.Enabled = False
    butSend.Enabled = False
    'etcetera
End Sub

Private Sub Form_Load()
    butInit.Caption = "Initialize"
    ' could disable all buttons that need a connection
    ' for example have that code in the disconnected event and call it here
    Call dmh_Disconnected
    ' and then enable buttons in the Connect() event
    ' connect using property values
    Call dmh.Init( )
End Sub
 

The second style of error handling is to add explicit error trapping to your procedures.  This is the safest style since an untrapped error will end your program.   An example procedure with error trapping follows:

Private Sub butClose_Click()
    'this button stays enabled when there is no connection so it
    ' needs error trapping
    On Error GoTo butClose_Err
    dmh.CloseMailbox txtreplybox.Text
    Exit Sub
butClose_Err:
    MsgBox "Trapped Error " & Err.Number & vbCrLf & Err.Description
End Sub

If your application does asynchronous receiving, you should probably avoid using the MsgBox call.  During our testing we found that receiving did not work if a MsgBox dialog was being shown.  The good news is that receiving works fine if a VB modal form is being displayed.

Here is a summary of the kinds of errors that the DMH control will raise.  When you make a method call that requires a DMH server connection, and you do not have one, the method call will exit with error 40006 being raised.  If you use an improper mailbox name such as one with whitespace in it, the method call will exit with error 380 being raised.  These are the two main errors when initiating a method call.  These errors are raised using the Err.Raise( ) facility of Visual Basic.  Most of the DMH control method calls are then processed asynchronously.  In other words, your method call returns, and the message communication you initiated happens as events are processed.  An error that occurs during event processing results in the DMH control Error( ) event occuring.  A DMH Error( ) event is different than an error raised by Err.Raise( ).  You do not need to write an Error( ) event handler, and these events do not end your application.  In contrast,  an error created by Err.Raise( ) will terminate your program unless it is trapped with an On Error statement.  You probably should write a handler for the DMH Error( ) event to communicate to the users of your program errors that occur.  When the Error( ) event occurs, the Disconnected( ) event will also occur.  You can place logic in your Disconnected( ) event handler to initiate recovery and resume logic, exit the program, etc.

Child Forms

Here is the recommended technique to use a single DMH control with all of the form code of your application.  If you have a child form (window) with the name subtest, include a variable for use in referencing the parent form DMH control.  So in the child form declarations, you include:

' Child Form declaration
Public dmh As DMH
'Child Form code
Private Sub butChild_Click()
    Dim reply as String
    reply = dmh.DoXact("DATAHUB", "eval localtime 16");
    ' etc
End Sub

And the logic in your main form will look something like this:

' Parent form declaration
' a child window -  how to access the dmh control
' that exists on the parent
Private subwin As subtest

Private Sub Form_Load()
    ' have an example child window that can also use our DMH connection
    ' you should share a single DMH control as in this example
    Set subwin = New subtest
    ' you MUST use "Set" in the object assignment
    Set subwin.dmh = dmh
End Sub

Private Sub butSubtest_Click()
    subwin.Show vbModeless
    'subwin.Show vbModal, Me
End Sub

In the code for the child window logic, we use the variable "dmh" just as we do when coding logic for the parent form.

Sending and Receiving - Basics

The programming model of the DMH message system is that you send a plain text message to a destination mailbox.  When sending the message, you can optionally specify the name of a second mailbox for the recipient to send a reply message to.  Usually you send a message to an application logic server such as a Datahub.  The message is usually SQL,  Tcl, or VFEI code that the recipient executes.  If the sender has designated a reply mailbox, the result of executing the command is sent to the reply mailbox.

Lets discuss sending without asking for replies using the Send( ) method.   For example,  suppose you are integrating a barcode reader.  When data is read from the barcode reader device, your code is supposed to update a record in a Datahub table.  You will send messages without asking for reply messages - if there is a system shutdown or communication failure, your application will know from the Disconnected( ) event.  This is more efficient than asking for a reply message at every barcode read.

' update latest read record in table barcode_reader at the Datahub
Dim message as String
message = "update barcode_reader set data_in='" & newdata & "' where device_id='" & myID & "'"
call dmh.Send(Hub, message)

When you send a message without waiting for a reply, it is referred to as an asynchronous send.

Often, you will want to send a message to obtain reply data.  The most convenient method to use is the DoXact( ) method. This method will take care of specifying and using a unique mailbox for your reply message, and it will take care of managing a timer in case a reply message is not forthcoming.  Suppose you want to query a database table, and the DB variable is assigned the mailbox name of the dmh_SQLsrv process.   The online documentation shows that the SQL standard "select" command sends multiple reply messages, but the "telect" command sends a single reply message with all of the requested data formatted as a Tcl list.  The DoXact( ) method is designed for a single reply message, so your code looks like:

Dim reply as String
reply = dmh.DoXact(DB, "telect device_id from barcode_config where display='" & dmh.LocalHostname( ) & "'")
if reply = "TIMEOUT" then
    ' ...
    Exit Sub
End If
' Parse the Tcl List
' element(6) = rows of data, then (0) = first row, then (0) = first item in row
device_id  = dmh.ListElement(reply, 6, 0, 0)

Sending and Receiving - Advanced

If your application needs to receive unsolicited messages from other processes, you use the Whenever( ) method to setup asynchronous receiving.  When an unsolicited message is received, your MessageArrival( ) event handler is called.  If you are receiving to multiple mailbox names, you will need to code a Select/Case statement or some if...then...else logic in your MessageArrival( ) event handler.  By convention you should use mailbox names for receiving that end in _SQL if SQL messages are expected, _VFEI if VFEI messages are expected, and _RPC if Tcl messages are expected.   There is no limit to the number of mailboxes that you use for receiving, but the design convention is that you use unique names based on the server function(s) provided.  There should be only one receiving process per mailbox name in a DMH group.  You may wish to create a unique mailbox name for receiving by basing the name on your hostname - see the DMH method LocalHostname( ).

The Whenever( ) method is used for ongoing receiving - your application will continue to receive the messages that are sent to your specified mailbox.  The Disarm( ) method can be used to stop asynchronous receiving.

The Whenmsg( ) method is similar to Whenever( ) except that it functions to receive only one message.  If Whenmsg_Again( ) is executed from the MessageArrival( ) event handler, then the control is re-armed to receive the next message.  So the combination of Whenmsg( ) and WhenmsgAgain( ) are equivalent to the Whenever( ) method.

Lets revisit the DoXact( ) method.  In some situations, you may want higher performance by sending messages and collecting the replies asynchronously, instead of waiting for each reply before sending the next message.  You do this by setting up one or more reply mailboxes and arming them for receiving using the Whenmsg( ) or Whenever( ) methods.  Instead of using DoXact( ) use the Send( ) command and specify the reply mailbox argument.  Typically a high  performance application will create a small number of unique mailbox names for replies, and re-use them.  If you are creating unique mailboxes for each reply message, use the CloseMailbox( ) method when you are done with each one, to recover resource usage.

Event Handling Notes

Your application should not "live" inside of your event handling code.  Be sure to return in due course.  Avoid calling DoEvents or performing long running computations.

Be careful with the Trace( ) event.  If you turn on character conversion tracing and are exchanging long messages, your application will be manipulating huge amounts of string data.

Do not call MsgBox( ) or invoke similar modal windows from inside of an event handler.  Instead, have a list box or text field on a Form window that you append to.

International Character Sets

International characters can be part of your data messages that you exchange with Tcl applications and store in Datahub or ODBC tables.  The DMH control translates the Unicode wide characters used in VB to the multibyte UTF-8 characters used by Tcl.  When declaring database tables to hold international text, base the VARCHAR( ) sizes on UTF-8 byte counts, not on the number of characters.  In the most conservative case,  you need to allow 3 bytes per displayed character.

Lets do a "hello world" example.  On a US English keyboard, the character é (e with an acute accent) is entered by holding down the Alt key and typing 1 3 0 on the numeric keypad.   Start the Datahub by clicking on the Tcl 8.3 program item.  Start the TestDMHProj.exe test program.  Click the Initialize button to connect to the Datahub.  In the entryfield for "Message or Input Data", type the command

eval set hello héllo

Press the "DoXact(Mailbox,Message)" button to send this to the Datahub.  This command tells the Datahub to evaluate the Tcl statement "set hello héllo".   You will see the text "DoXact( ) Reply:héllo" in the "Reply or Output Data" field.  Bring up the Datahub console window using the menu item "File/Tcl command..." on the Datahub application, or just send the message "eval console show".  On the datahub console, type the command "set hello".  This statement asks to display the value of the global variable hello.  You should see the text result héllo.  If you type the command "string length $hello", the result is the value 5.  If you execute "string bytelength $hello" the result is 6.  These results tell you that the value of the hello variable is a 5 character string that uses 6 bytes for its internal representation.  If you turn on String Conversion tracing on the TestDMHProj.exe program, you will see that the é character is represented in Tcl using the two bytes (hex C3 and hex A9).

For Internationalization to succeed as well for your language(s), you need to make sure that you have installed fonts and chosen fonts in your application that are capable of displaying the characters you require.

Termination

When your application is shutting down, you should call the Disconnect( ) method to disconnect gracefully from the DMH server.  Practically speaking the DMH server routinely takes care of situations where clients leave ungracefully, but proper software manners are encouraged.

 

Miscellaneous Notes

An actual network connection is not attempted until Init() is called.

The programming model is that you will not have more than one connection to a particular DMH server.  It is typical to have only one DMH connection per application process.  Communication across DMH groups can be accomplished by sending to mailbox@groupname.  It is also possible to use multiple controls, each connected to a different DMH Group.

There should be only one reader per mailbox name in a given DMH group.

The VB DMH control is able to use and resolve DMH groupname aliases as described in the online Tcl documentation.  Groupname aliases are resolved at the DMH server and not at the client.

Mailbox Naming rules:



DMH.ocx Control Reference

 DMH.ocx Control Properties
Property Name Data Type Description
DefaultTimeout Long The default timeout interval for send and reply transactions, or timed receive invocations.   In seconds - the default value is 30.  Settable range: 1 - 86399.
RemoteGroup String The name of the DMH server message group, such as "PIC".  The DMH software maps the specified groupname to a TCP/IP socket port.  The value may also be specified as  numeric text which is interpreted as directly specifying the TCP/IP port the DMH server is using.  The default value is "mbx" which enables connection to a Datahub program started with default values.  This property can be set at design or runtime, or set when calling the Init method.
RemoteHost String The TCP/IP hostname of the DMH server.  The default value is "localhost" which is a standard way to specify the computer that the control is executing on.  The value can also specify an IP address such as "192.168.2.12".  This property can be set at design or runtime, or set when calling the Init( ) method.
State Enumerated
Integer
The state property is a read-only value available at runtime.  The value can be read to determine if a healthy communication connection exists, etc.    Applications will ordinarily use event methods and not poll the state property value.  The transient and error states may not last long, so testing for the values 0 or 7 would be the most common scenario.
  0       disconnected
  4       hostname is being resolved
  6       connection setup in progress 
  7       healthy connection exists
  8       connection close in progress
  9       a communication error has occured  
519       DMH protocol setup in progress (519 = &H100 Or 7)
Tracebits Long The Tracebits property controls output of diagnostic data to the Trace event.  This value is used as a bitfield with the bit values controlling the following categories of output:

01 data reads
02 data writes
04 message receiving
08 message sending
16 logic tracing
32 UTF-8, BSTR character conversions

mhgroup String The mhgroup property is a readonly runtime value indicating the hostname:port  of the DMH server when connected, else as empty string.  Hostname is the server's idea of his hostname which may be different from the RemoteHost that was specified at the client.


 
DMH.ocx Control Generated Events
Event Signature Description
Connected( ) Happens after successfully connecting to the DMH server in the wake of the Init method invocation.
Disconnected( ) The DMH connection has been closed from any circumstance such as remote closure, communication failure, error, or invocation of the Disconnect method.  This event is similar to the Tcl lostserver procedure invocation
Error(
error number as Long, 
description as String)
The Error event happens when the Init method fails, or there has been communication failure.  In most cases when the Error event happens, the control state will transition to the disconnected state, and the Disconnected( ) event will occur shortly.

Init errors include

10051  the network is unreachable
10060  Connection has timed out. This error indicates that the remote host may not be online.
10061  Connection attempt is refused. 
10061  Connection is forcefully rejected.
The remote host is online and reachable, but there is not a DMH server at the specified group or port.  In some cases we have improperly seen this error when using the hostname "localhost", and have worked around it using the actual hostname.  This seems to be a buggy behavior that is being investigated.
11001  Hostname not found (DNS authoritative).  This error may indicate an improperly spelled hostname, or a hostname that is not known to your name server.
11002  Hostname not found (non-authoritative name resolution).  This error may indicate that your name server is down.
50003  DMH Server refuses our client connection
This error indicates that customer modified software running in the DMH server has rejected the connection.  It is likely that you are in violation of your site's security policy.
50004  DMH Protocol error - improper setup reply.
You should never see this error.


Broken connection errors include:

10051  the network is unreachable
10060  Connection has timed out
11053  Connection is aborted due to timeout or other failure
10054  Connection is reset by remote system
10058  Connection has been shutdown
50001  DMH Protocol error - missing data.  You should never see this error.
50002  DMH Protocol error - improper packet.  You should never see this error.
Attempting to use a method that requires a connection when a healthy connection does not exist results in the error:
40006  Wrong connection state for the request
40006  No DMH Server Connection
MessageArrival(
DestinationMailbox as String, 
ReplyMailbox as String, 
Data as String)
A message has arrived.  The DestinationMailbox parameter is a mailbox name that your application has specified when initiating receiving.  If the sender of the message indicated a reply mailbox, it is passed as the second argument, ReplyMailbox.  If no reply mailbox has been specified, the ReplyMailbox argument is an empty string.  The Data argument is the text of the sent message.  The DMH control's logic protects you from receiving another message for the DestinationMailbox, and re-entering your MessageArrival logic until you have returned from the MessageArrival( ) call

String data is represented in Tcl as multibyte UTF-8 character sequences, and as double byte character sequences in Visual Basic.   Conversion between the two representations occurs transparently. 

Shutdown(ByRef StayAlive as Boolean) A remote request has been received to terminate the process.  If you do not set the StayAlive flag true, the control will cause the application to disconnect from the DMH message system.  Because of implementation constraints the control is not able to cause your application to exit.  If you want to exit, you need to write a Shutdown event handler that executes "End" or "Unload me".
Trace(Message as String) This event provides diagnostic and debug information per the Tracebit property setting.  Your application needs to avoid creating new DMH activity in the Trace event callback, that in return causes Trace events.  A cycle of positive feedback is possible which will cause a software fission reaction.

DMH.ocx Control Methods
Abort( ) Any in-progress send-and-reply or modal wait transactions such as the Init( ) method or DoXact( ) calls are aborted with return values indicating TIMEOUT.  Invoking the Abort method does not affect asynchronous receiving that is setup using the Whenmsg( ) or Whenever( ) methods.
CloseMailbox(boxname ) Stop using a mailbox - Disarm receiving if listening, Flush if not empty,  and remove from existence if it exists.  The Tcl version of this call, differs because it will not flush existing messages.

You must be connected to use this call.

Count(boxname) as long(3) Returns a three element array giving the total count of messages that have been sent to the mailbox, the total count of messages that have been consumed from the mailbox, and last, the current count of pending messages.  A pending message is one that exists in the queue associated with the mailbox, and has not been consumed by reading or flushing.

You must be connected to use this call.

Disarm(optional boxname as String) Un-register the listener from a specified  mailbox.  This call may be used to cancel an earlier whenever( ) or whenmsg( ) call.  If called with no arguments, all Whenever( ) and Whenmsg( ) receiving registrations are cancelled.  The Abort( ) method will cancel in-progress DoXact( ) calls.

You must be connected to use this call.

Disconnect( ) The counterpart of Init( ); disconnects from the DMH server.  The Abort( ) method gets called to end any in-progress transactions.  The Disarm( ) method gets called with no arguments to cancel all receiving.
DoXact(DestinationMailbox as String,
Message as String, 
optional TimeoutSeconds as Long,
optional ReplyMailbox as String) 
As String
Performs a complete send and reply transaction with timeout management.  Creates and manages a unique reply mailbox for the send and reply transaction if the replybox argument is defaulted.  If the timeout is not specified, the DefaultTimeout value is used.  The usual reply is the text of the reply message. The String literal TIMEOUT is returned in case of failure.  You can only have one instance, of DoXact( ) or TimedReceive( ) active at a time.  If you specify a ReplyMailbox, you need to insure that the name you specify is only used by your application.  It is usual to create a unique reply mailbox name, perhaps based on the hostname, assign it to a variable, and use it repeatedly.

You must be connected to use this call.

Flush(MailboxName) Empty a mailbox of any pending messages.  A pending message is one that has been sent to the mailbox but has not been consumed.  In other words, a pending message is waiting in a queue associated with the  mailbox name.  Messages are consumed by reading or flushing.

You must be connected to use this call.

GroupnamePort(Groupname as String) 
As Long
Used to determine the TCP/IP port number that is used by the DMH server to listen for client connections.  The method is equivalent to the mh_name_to_socket Tcl procedure.  Most applications will not have a use for this method since the server socket port is managed by the DMH software. 
Init(Optional DMHGroupname as String, 
Optional RemoteHostname) As Long
Performs the initial connection to the DMH message server. The connection will be setup or an error result will be obtained before returning.

If no DMHGroupname or RemoteHostname is specified, the control property values are used. 

If the connection succeeds, the return value is 0.  A non-zero result is an error number indicating why the initialization failed.  See the Error( ) event for specific codes.

When the initialization is successfully completed, the Connected( ) event is fired.  If the Init call fails, the Error( ) event is fired. 

If the connection to the DMH server is ever lost, the Disconnected( ) event is fired.

ListElement(TclList as String, 
index1 as Long, 
optional index2 as Long,
optional index3 as Long ) As String
This method is similar to the lindex method of Tcl.  It will parse text formatted as a Tcl list and return the specified element.  Additional indexes may be specified to parse nested lists.  If a specified index is out of bounds, an empty string is returned.  Error 50008 is raised if an invalid list is parsed. 

Dim reply as String
Dim item as String
reply = dmh.DoXact(Datahub, telect_query)
' index 6 = rows of data, 0 = first row, 0 = first item in row
item = dmh.ListElement(reply, 6, 0, 0)

LocalHostname( ) as String Returns the TCP/IP hostname of the computer that the control is executing on.  The name is guaranteed to be stripped of domain information, and imbedded white spaces which are not valid in hostnames are replaced with underscores.
ReceiveList( ) as String Returns a list of the mailboxes you are listening for messages on.  Does not show mailbox names that have in-progress MessageArrival( ) events.  This command may be useful for debugging, and it is not used in a typical application.

You must be connected to use this call.

Send( DestinationMailbox as String,
Message as String,
optional ReplyMailbox as String)
Send a message to a mailbox, optionally with a reply mailbox indicated.   By convention, when a reply mailbox is indicated for a command message sent to a Datahub mailbox or equipment interface mailbox, the command is processed, and a reply message is sent to the reply mailbox. 

Specifying the reply mailbox as an empty string or as the literal value "NULL" is equivalent to not specifying a reply mailbox.

Sending to the mailbox name "TRACE" sends the message to the DMH Server Trace Facility.

The Send method corresponds to the Tcl mbx put and mbx putr commands.  Note that "Put" is a keyword in Visual Basic and could not be used for this method.

You must be connected to use this call.

ServerStatus( ) as String Returns a Tcl list containing the information presented in the Tcl DMH status window.  The information can be parsed by the application to determine  status information on every mailbox that is currently in use.  This command may be useful for debugging, and is not used by ordinary applications.

The first element of the list is a list of 5 elements:
{ hostname:port messages_received messages_sent messages_queued tcl_version }

Subsequent elements in the list are lists of four or five elements:
{ mailboxname count_in count_out count_pending [reader_handle] }

Additional elements may exist in the list if there are DMH clients that are not currently waiting to receive messages.  These elements are formatted as:
{{{no whenmsg pending}} - - - reader_handle}

You must be connected to use this call.

TclJoin(elements( ) as String,
optional joinstring = " ")
as String
Joins together strings as Tcl list elements forming a result string that is a Tcl list.  Similar to the Join( ) command of Basic except that braces are added as needed to delimit empty elements, or to delimit special Tcl character sequences involving backslashes , square brackets, etc.
TclJoinV(elements as Variant,
optional joinstring = " ")
as String
Same as TclJoin( ) except the list elements are passed as a Variant array.  The Variant array is also the output type of the array( ) function.

Dim s as String
s = TclJoinV(array("hello world", "arg 2", "last"))
 = "{hello world} {arg 2} last"

TclSplit( Tcllist as String, 
ByRef elements() as String,
optional ByRef ErrorInfo as String) 
as Long
TclSplit( ) parses a string formatted as a Tcl list into an array of string elements.  The method is similar to the Split( ) function of VB except that it understands the Tcl usage of quotes, braces and backslash sequences.  Not all strings are valid Tcl lists.  The return value is 0 on success.  In case of error, the elements array is set to an empty array, and the return value and ErrorInfo are set as follows:

return value, ErrorInfo
0, ""
1, list element in braces followed by non-whitespace
2, list element in quotes followed by non-whitespace
3, unmatched open quote in list
4, unmatched open brace in list
 

TimedReceive(ReceiveMailbox as String,
optional TimeoutSeconds as Long) 
As String
Waits for a message to be received in the specified mailbox.  If the call succeeds,  the return value is the message data.  If the call fails, the return value is the literal string "TIMEOUT". 

You must be connected to use this call.

Version( ) as String Current software returns the string "1.1" to  indicate compatibility with DMH versions 1.1 and earlier.
VFEI2Dictionary(VFEItext as String,
ByRef NameValues as Dictionary) as Long
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

The VFEI2Dictionary method parses a SEMATECH VFEI formatted text string into (name, value) items in a Visual Basic Dictionary.  VFEI is a SEMATECH standard for the format of messages used for equipment integration. This command parses a VFEI version 2.2 (or 2.1) message for the name and value of the data items.  The return value is the count of items that have been added to the dictionary as a result of parsing the text.  This implementation succeeds in parsing VFEI messages that do not strictly adhere to the SEMATECH description. In general, quotes are only needed to delineate a data item that has imbedded white space. There is no problem with additional white space between any of the lexical elements. The "/" and format code are optional and may be omitted.
 

Private Sub butVFEI_Click()
    Dim dic As New Dictionary
    Dim tsts As Variant
    Dim key As Variant
    Dim rc As Long
    Dim text As String
    Dim teststring As String
    On Error Resume Next
    tsts = Array( _
      "DESC=|The VFEI parser can parse name = value strings!| ", _
      "Filename=|dmh.ocx| directory=|\\server\path1\path2| ", _
      "Command=|you can have \|imbedded quotes\| | ", _
      "CMD / A = |ALARM SETUP|  MID/A=|STP1| MTY/A=|C| TID/U4=2 ENABLE/U1=0 ALARM_ID/I4[3]=[ 1 |2|  3 ]", _
      "CMD/A=|INITIALIZE| MID/A=|STP1| MTY/A=|C| TID/U4=1", _
      "CMD / A = |ALARM SETUP|  MID/A=|STP1| MTY/A=|C| TID/U4=2 ENABLE/U1=0 ALARM_ID/I4[3]=[1 2 3]", _
      "xx/U1 = 0 xU2/U2=0 xI4/I4= 1234 xI8/ I8 = 2 xF4/F4 = 1.2 xBL/BL=0", _
      "CMD/A=|INITIALIZE| LL/L[3]=[MID/A=|STP1| MTY/A=|C| TID/U4=1]", _
      "CMD=INITIALIZE MID=STP1 THING1=|hello world| thing2=355 thing3=6.02e23", _
      "CMD/A=TEST5 REPORT2/L[3]=[EVENT_ID/A=|MB_COMPLETE| CLOCK/A=|1992| XX/L[3]=[FIRST/A=|TEXT 1st| SECOND/U2=255 THIRD/F4=0.003]]", _
      "")

    text = ""
    For Each tst In tsts
        'replace | with quote (cannot imbed quotes directly)
        teststring = Replace(tst, "|", Chr(34))
        text = text & teststring & vbCrLf
        rc = -1
        Err.Number = 0
        rc = dmh.VFEI2Dictionary(teststring, dic)
        text = text & "rc=" & rc & vbCrLf
        For Each key In dic
            text = text & key & "=" & dic.Item(key) & vbCrLf
        Next
        If Err.Number <> 0 Then
            text = text & "trapped error: " & Err.Number & Err.Description & vbCrLf & vbCrLf
        Else
            text = text & vbCrLf
        End If
        txtoutput.text = text
        dic.removeall
    Next
End Sub

Whenever(MailboxName as String) Registers to receive all messages directed to the specified mailbox.  When the MessageArrival( ) event handler returns, the control re-arms for receiving the next message directed to the specified mailbox.  The Disarm( ) method is used to stop receiving.

You must be connected to use this call.

Whenmsg(MailboxName as String) Register for receiving the next available message directed to the specified mailbox.  Calling WhenmsgAgain( ) in the MessageArrival( ) event handling code re-arms the receive registration for the next message.

You must be connected to use this call.

WhenmsgAgain( ) The Whenmsg( ) method functions as a one-shot.  In other words, receiving is stopped after receiving one message.  Calling the WhenmsgAgain method from the MessageArrival handler re-registers to receive the next message.

You must be connected to use this call.


Err.Raise Method Errors

These are errors that may be raised as a result of a method invocation.

    380    mailbox name must start with an ASCII letter, digit, or underscore
    380    mailbox name must contain only ASCII letters, digits, -, _, ., !, :, or @
    380    The property value is invalid.
40006    No DMH Server Connection
40006    Cannot re-enter synchronous send and reply logic
40006    Cannot initialize in current state
50005    Already processing a DoXact( ) call for this ReplyMailbox
50006    Timeout or other error obtaining count
50007    String to byte conversion failure
50008    improper Tcl List
50009    invalid VFEI text
50010    improper use of NULL mailbox


License Terms


Subject to Change Without Notice

The VB DMH Active-X control is licensed for development and runtime use at no additional charge for computers that are licensed for development use of the Hume Integration Datahub SDK.  We ask that VB developers install the Tcl executables and actively use the Tcl executables for testing and development, instead of developing against their factory's production servers.  Also, we ask that VB developers install the Tcl online documentation and use it to supplement the material presented in this document.

Hume Integration is also pleased to offer separate runtime licenses for using the DMH control on systems that are not licensed as development systems. Runtime usage of the DMH client software is licensed separately from the Datahub SDK runtime license.


References

The author wrote his first Basic program in 1966 using his father's Teletype dialed in to an IBM timesharing system at 115 baud or so.   The language has changed quite a bit, and the following books were found helpful.
  1. VB & VBA In A Nutshell: The Language by Paul Lomax, O'Reilly & Associates Inc. 1998.  This book was by far the most useful.  It is awkward to use the online VB documentation because each topic is a separate document and the navigation between related topics is difficult.
  2. Visual Basic Controls In a Nutshell: The Controls of the Professional and Enterprise Editions by Evan S. Dictor, O'Reilly & Associates Inc. 1999.
  3. Hardcode Visual Basic Second Edition by Bruce McKinney, Microsoft Press, 1997.  I wish this book was updated for VB6, but it is based on VB5.  Nonetheless it covers topics that the above references do not address.

Document Version

Date of last revision: $Date: 2017/08/10 18:22:08 $