Wrapping Legacy Code with Visual Basic 4.0 Objects

Tom Gordon
Microsoft Consulting Services, Microsoft Corporation

October 5, 1995

Abstract

Practicality dictates that you don't discard something of value. Much of the code developed over the past 20 years still has value and would cost enormous amounts of money to rewrite. What is the solution? Begin back where we started? No, that would be prohibitive. Prevent any new development with older languages? That is unlikely as well, considering that much of what we call new development is in fact an enhancement of older systems. Reuse and recycle existing code? Maybe that's a solution! Code that has functioned for many years could be a candidate for reuse. Of course, the code would need to work in a microcomputer environment to be a target for current platforms. That, however, is entirely possible because both the logical structure of existing applications and the code itself can be ported to small-computer environments.

The Microsoft® Visual Basic® programming system version 4.0 provides many new ways of integrating legacy code into new applications. Its ability to create objects enables developers to disguise the technical details of the legacy-code interface in a set of properties and methods.

Why Use Legacy Code and Languages?

As addressed in this paper, legacy code is code typically written in a third-generation language, such as COBOL or Fortran, and structured in a certain way. That is to say, it's not just the language used but also the structural approach that defines legacy code. For example, a serially executed program written in the C language can often be considered a legacy application—although most legacy code is written in COBOL or Fortran. A number of factors argue for the continued use of legacy code. Two of the most compelling are the huge quantity of existing code and the valuable skills of legacy developers, which have been acquired over many years of creating code and which should be leveraged.

Billions of Lines of Code

There are reportedly billions of lines of COBOL code in existence, with millions more created every year. The majority of large-systems development is still done using what are considered legacy languages. This is due not to a misunderstanding of the direction of current technology but to a basic reality in business. Many of the existing systems in the corporate world are implemented in these languages, and most development staff members still use them. Even if all development of these systems halted today, maintenance issues would extend well into the next century.

Fortran still rules in the scientific and engineering fields as an ideal tool for the creation of technical software. It cannot be ignored, because the structure of the language lends itself to extremely efficient computing, especially for precise vector and statistical analyses. Much of the code written in Fortran fits well into a modular development environment.

An increasing amount of new development is being done with newer languages, but therein lies the problem. Legacy code contains many of the business rules describing how companies operate. This code can be translated into new languages, but that requires the retraining of development staff and significant rewrite time. It is not a simple task to train some legacy developers in the use of current technologies and languages, especially in a move from COBOL to C++. It is possible to retrain those individuals in Microsoft® Visual Basic® version 4.0, however, and continue to leverage skills they have gained in the development of legacy code.

To take advantage of those skills requires that legacy code be encapsulated into reusable modules for access by current languages. For this process to occur, however, how the legacy code functions must first be understood. Such understanding may be hard to come by, because a large percentage of these older systems may not be totally understood by the development staff, whose knowledge of the code is typically third-hand.

Some applications written in legacy code, typically report generators and utility functions, can execute in a stand-alone mode. If the source code for such applications is available, then with usually minor modifications the applications can be great candidates for resurrection as modules. By a modification of the approach used by the code, the application can become a replaceable module that later, if necessary, can be rewritten in another language.

Even if legacy code is not understood (or doesn't exist), new code can be written in legacy languages by developers who are not inclined to work with the newer languages. Such an approach will enable the skill leveraging of individuals who work well with legacy systems by applying their skills to both maintenance of older systems and new development. Over time, such individuals are likely to learn more and more about the newer languages and to become proficient in new technologies.

Leveraging Development Skills

A large number of corporate developers are still working with legacy code. They maintain and develop enhancements to systems typically written in COBOL, RPG, BAL, and Fortran. To perform their jobs well, they need a considerable amount of skill and practical experience. Such experience should not be lost in the shuffle of information systems (IS) shops to move to new technologies. If fact, these skills can be used to enhance the functionality of new systems being created. Each skilled employee who leaves when his or her skills are perceived to be obsolete is lost as a corporate asset—a loss that can have real impact on the bottom line. The idea is not to change what such developers do but rather how they approach the systems they create.

For example, even if a large IS shop should decide to use only state-of-the-art development languages from today on, that shop would still need to apply a considerable amount of effort to maintaining its current legacy code. Usually the current development staff handles this task, for two reasons: (1) it is dangerous to drop all current maintenance; and (2) most maintenance personnel prefer not to limit their work to maintenance alone—they also want to get involved with new systems. Using the approach outlined in this paper, they can do both: They can integrate their skills into new systems development by working with new development teams and contributing their knowledge and skills to those teams.

Traditional vs. Component-Based Application Design

Development teams experienced with legacy code need to be trained into thinking how to create systems in small pieces. This approach differs dramatically from traditional practices of teams coding in languages such as COBOL. According to those practices, developers create large, monolithic systems with few separable parts. Most code reuse in such systems depends on the maintenance by developers of individual libraries of code from past projects. Unfortunately, this approach does not lend itself well to code-sharing among individuals. Figure 1 shows a traditional application design using a third-generation language, or 3GL.

Figure 1. Traditional application design using a 3GL language

On the other hand, the traditional approach matched perfectly the structure of computer technology for many years. Most systems were large, central-processing machines with many input-output terminals. All processing took place within a single physical device, and all storage and peripherals resided within the same physical space. Figure 2 diagrams a basic configuration of such systems.

Figure 2. A high-level look at mainframe architecture

As computer technology has progressed, hardware has moved into smaller, networked platforms. This move has in turn enabled software systems to move into a client/server framework, where processing is distributed among several platforms. This approach enables processes to be isolated from one another, with known interfaces linking their functions. Figure 3 shows a classic client/server architecture.

Figure 3. A classic client/server architecture

The client/server approach is efficient because it enables the development of each system component to take advantage of the hardware platform upon which the component is to be deployed. Of course, such an approach requires a change from the traditional approach to systems construction. Instead of considering software in terms of large, monolithic systems, developers can begin to look at it as a set of components that provide services.

Figure 4. A services-based architecture

The Microsoft Solutions Framework promotes the development of software as a set of service components that can be accessed by other components. Client/server architecture facilitates the development of discrete functions among systems, enabling a very structured approach to development. Figure 4 shows a system that is developed as a group of components. These service components are executable code rather than source code, which means that they can be shared and reused as completed, tested, and replaceable objects.

Following the Microsoft Solutions Framework, the development process is divided into a set of smaller projects, each creating a reusable piece of the system. Early in the development cycle, these pieces should be identified and launched as separate modules (or components). The goal of these projects is to create reusable modules both for the current system and for other systems—an approach that practically ensures a high degree of reuse.

The division between services in a services-based architecture depends on the application. Business services, for example, often reside on a server but also can reside on client computers.

Development teams creating applications for a services-based architecture consist of two separate groups: solution builders and component builders. Solution builders translate user and business needs into discrete concepts and specify components representing these concepts. Component builders create the components by turning out high-quality code that is both reusable and stable. Solution builders then draw upon the new components—as well as existing ones—and "glue" them together into an application.

Component-Based Design Techniques Using the Dynamic-Link Library

This paper describes how legacy code can be compiled into functions and routines that can be directly addressed by Visual Basic 4.0 and then encapsulated into Visual Basic 4.0 objects. Such objects retain the functionality of the original code while featuring the simplicity of implementation and use that is characteristic of code created with Visual Basic 4.0. The result is a win for developers in two critical areas: ease of development for legacy developers and ease of use for developers using Visual Basic 4.0.

There are several steps in the process. The first is to change the legacy code into what is known as a dynamic-link library, or DLL. The second is to "wrap" the DLL in a Visual Basic 4.0 OLE object and test the DLL within an application based on Visual Basic 4.0. The third step is to turn the DLL into an independently executing object that provides the services of the DLL to any OLE-enabled development tool.

The Role of the DLL

A DLL is a set of compiled code that is linked into a set of executing code at run time. Statically linked libraries are linked into other code at compile time. The former implies that the application is developed in several pieces, while the latter suggests the final product is a single executable file. A DLL is a type of component.

Static linking differs from this "dynamic linking" in two primary ways: First, static linking makes all code modules to be stored within the .EXE file during the compile-and-link cycle: there's no need for any other executable files to make the executable routine "whole." Static linking also yields executable modules that are larger and more costly to maintain.

When using a DLL, a program proceeds through the compile-and-link cycle and is told which routines are missing and which DLL holds those routines. The final executable program that is created by the compiler does not have the code that exists within the DLL but rather only the name of the DLL and a pointer indicating the location of routines within the DLL.

When the program is executed, the loader looks for the DLL and finds the code routines needed by the program. Those routines are made available in memory for the program to use.

DLLs also can be changed so as to modify the functionality of the calling program without having to replace it. In addition, DLLs can be shared by several applications. Take for example a screen driver. Code for a screen driver could be compiled into every executable program within an operating environment, but that would be impractical. Implemented as a DLL, such code can be in a separate file that can be changed or updated to accommodate changing display hardware.

DLLs provide other advantages as well. If a certain routine uses numerous options for setup, all of that code would need to be in a statically linked executable file; a system designed with the use of DLLs would load only the code needed to service the selected options. Such an approach can be very helpful when dealing with such code as screen drivers.

The Declare Statement

The Declare statement is the vehicle through which Visual Basic 4.0 communicates with a DLL. The Declare statement can be used in the Declarations section of a form or code module. Through the Declare statement, a function or subroutine is made available to Visual Basic 4.0 just as if it were a routine intrinsic to the Visual Basic 4.0 language. The following is a typical Declare statement:

Declare Sub CREATE Lib "acctstat.dll" _
(ISAMFileStatus As FileStatusType, TargetPath As PathType)

Note that the routine is named CREATE and is located in the library file ACCTSTAT.DLL. The routine uses two parameters: a user-defined type called FileStatusType and a user-defined type called PathType. The routine has no return and therefore is declared as a Sub.

After using this statement, Visual Basic 4.0 can now use the routine CREATE.

Any DLL function created on a custom basis needs an appropriate Declare statement. Creation of such statements is not difficult for the experienced developer; however, it can be a tedious task without some low-level knowledge of the called routine. (The OLE encapsulation process described later in this paper should help resolve such difficulty.)

Calling the DLL

Invoking the DLL from Visual Basic 4.0 is a simple process, identical to that for calling any other function or subroutine within the language. The following code segment, for example, calls the preceding CREATE routine:

gTargetPath.PathString = gsIsamFileName
Call CREATE(IsamFileStatus, gTargetPath)
MsgBox IsamFileStatus.Message

This snippet of code assigns the name of the file to create to the PathString element of the gTargetPath variable (which is a structure), and calls the CREATE subroutine. The return information is passed in the IsamFileStatus structure with the specific message in the Message element. Note that the use of structures is not necessary but is convenient when communicating with, in this instance, COBOL, which uses structured variables to a very high degree.

Note that the subroutine call in the preceding example also could have been accomplished in the following manner:

CREATE IsamFileStatus, gTargetPath

This alternative approach is perfectly acceptable to Visual Basic 4.0, although the use of Call makes the code easier to read.

Components of the DLL

Source code

The source code of a DLL contains code largely similar to that in any executing program. The only major difference is that the DLL does not have a "main" program. It is called as a function or a subroutine.

.def file

To define the functions and subroutines that execute in the dynamic-link library, a .DEF file is created to describe the major pieces of the interface. The function name is defined, but any parameters must be determined by the developer. This is where OLE helps simplify the calling scheme. The type can be handled in the OLE wrapper, insulating the developer from the details of the DLL. Please note that many compilers have techniques to create DLLs without .DEF files. This is handled by means of compiler-specific parameters on the line of the exported function.

Parameters

Specifying the parameters is one of the most complex processes involved in the definition of a DLL. If a parameter is not specified correctly, then the system will likely generate a memory fault and cause the termination of the executing application. It is critically important that you thoroughly examine the legacy-code documentation to ensure appropriate definition of parameter size.

The easiest language in which to create a compiled DLL is C. Although C is not really a legacy language (except for the most ardent fans of Visual Basic 4.0), it makes a good foundation. If you are experienced in using C, you have a basis for understanding DLL creation and OLE encapsulation. C also is a good language for developing highly optimized utility routines for Visual Basic 4.0. A good example of encapsulated, optimized C code is the Visual Basic 4.0 Remote Data Object (RDO), which hides the complexity of the Open Database Connectivity (ODBC) application programming interface (API) from the user, while making it easy to use.

Creating a Simple Wrapped DLL

To create a simple wrapped DLL, invoke the Microsoft Visual C++® development system version 2.2 or later and indicate that you want a new project. Specify that this is to be a DLL of the Microsoft Windows® operating system, and indicate where the project files are to go. You don't have to associate the project with any files initially. Please note that this is a 32-bit compile, so the arguments to establish the DLL export linkages are different than with a 16-bit compile.

You then create a source file by opening a new file and identifying it with the suffix .C. In the following segment is a simple source file that doubles the value of an incoming integer:

#include "stdarg.h"
long __stdcall _lDouble(long *lValue)
{
return *lValue + *lValue;
}

The return of the function is the doubled value, and the original value is not modified.

Compiling the DLL

From the Project menu, select Compile and make sure the Calling Convention is set to __stdcall. This calling convention, along with a .DEF file, is needed to ensure that the export name matches the name on the routine. The C++ compiler assumes that the function name will be used in a C++ program, so the name is significantly modified to fit that kind of syntax. You must override that syntax through the use of a .DEF file to override the compiler defaults.

The .DEF file used in this project (without the long qualifier) is as follows:

LIBRARY DOUBLE
CODE    PRELOAD MOVEABLE DISCARDABLE
DATA    PRELOAD MOVEABLE
EXPORTS
    _lDouble    @1

Running the DLL from Visual Basic 4.0

The DLL is linked into a program based on Visual Basic 4.0 with the following statement. Normally, it appears all on one line:

Declare Function lDouble Lib "h:\vboledll\winrel\DOUBLE.DLL" _ 
    Alias "_lDouble" (ByRef lValue As Long) As Long

Note   You must make sure you identify where the DLL resides. If it resides in a project directory, you should identify where that directory is explicitly. If the default project directory for Visual Basic 4.0 is the directory in which the DLL resides, you can enter the name of the DLL. The Windows operating system searches the \WINDOWS directory, then the \WINDOWS\SYSTEM directory, and then the path for DLLs if it cannot find them in the default directory.

Create a form with a text box (enter numeric "1" as the .Text property) and a command button. In the Click event of the command button, place the following code (which could be placed all on one line, but here is split for clarity):

Dim lValue As Long            'Declare a temporary variable.
lValue = Val(Text1.Text)      'Set the value of the variable.
lValue = lDouble(lValue)      'Call the DLL.
Text1.Text = Str$(lValue)     'Rewrite the value in the text box.

Run the code and note that the value in the text box doubles for each button click (until the value overflows).

Placing the DLL in an OLE server

Now that you have the DLL functioning, you can place it into an OLE server, a process that is greatly simplified by Visual Basic 4.0. Much of the mystery of the creation of OLE servers has involved such arcane issues as the creation of a type library and the registration of OLE server information. Visual Basic 4.0 takes care of those processes for you so that you can spend more time dealing with business issues instead of implementation problems.

As described here, the first object to be used is an out-of-process server. An out-of-process server has much more inherent functionality than an in-process server, because it can behave as a stand-alone program. It executes, and other process can access its functions. An in-process server runs in an application's memory space, providing faster data transfer but at the expense of some functionality. These trade-offs will be discussed in more detail later in this paper.

(The C routine just developed and performing such low-level functions would better fit into the space of an in-process server; however, because this is an example of DLL implementation in OLE servers, it's been kept simple by taking the process a step at a time. An in-process server example will follow.)

To place the DLL into an object is a simple matter. A project based on Visual Basic 4.0 must be created with a class module. The Declaration statement may appear in this module. A name for the object must be exposed for external access, and properties should be identified to enable parameter passing.

There are several steps you must take to ensure this will work:

  1. Expose the class by setting the properties for the class as follows:

    The Instancing property can be set to either single use or multiple use, but it should not be set to the Not Creatable option. The public property must be set if the object is to communicate outside its code space, and, of course, the Name property also must be set.

    The Creatable SingleUse setting forces a new copy of the server to be loaded each time the object is initialized in the application using the object. The Creatable MultiUse setting enables the object to be shared, with each created object sharing the same copy of the server. This is an issue because Visual Basic 4.0 is a single-thread system; that is, a multiuse server can be used by only one application at a time and during that time blocks all others that are trying to use its features.

  2. On the Tools menu, click Options, and select the Project tab. Set Project Startmode to OLE Server.

  3. Set Startup Form to be something like Main() in your module. You don't need a startup form unless you want to display some information.

  4. Set Project Name to be the exposed class name, in this case, DoubleClass.

  5. Set Application Description so that the Object Browser can examine the available objects.

  6. Write the Property Let and Property Get routines as follows:
    Option Explicit
    Private glNumber As Long
    -----------------------
    Property Let lNumber(lInput As Long)
    glNumber = lDouble(lInput) 'go ahead and double it
    End Property
    -----------------------
    Property Get lNumber() As Long
    lNumber = glNumber
    End Property
    
  7. Write the Module in the OLE server as follows:
    Option Explicit
    Declare Function lDouble Lib "h:\vboledll\winrel\DOUBLE.DLL" _     Alias "_lDouble" (ByRef lValue As Long) As Long
    ----------------
    Public Sub main()
    End Subsidiary
    
  8. Run the OLE server.

  9. Run a separate instance of Visual Basic 4.0 with the client code as follows:
    ' General Declaration section 
    Option Explicit
    Dim oDoubleObj As Object
    ' Code in the form 
    Private Sub Command1_Click()
    oDoubleObj.lNumber = Val(Text1.Text)
    Text1.Text = oDoubleObj.lNumber
    End Sub
    -----
    Private Sub Form_Load()
    Set oDoubleObj = CreateObject("doubleOOP.DoubleClass")
    End Sub
    

    If you cannot find the properties of the Visual Basic 4.0 object you are testing, click the Object Browser button on the toolbar and check the available properties and methods.

  10. Test to ensure the results are what you expect.

Accessing Fortran from an ole Object

Now that an out-of-process server has been created, create an application with a bit (granted, only a small bit) more semblance to reality: a statistical object that uses a Fortran DLL to perform the calculations. Because this is a utility program and has no user interface, it will be implemented as an in-process server. In this case, we'll use Microsoft Fortran PowerStation version 4.0 to create the DLL code.

The overall approach to creating an in-process server is not much different from the approach to creating an out-of-process server, except that an in-process server cannot create nonmodal dialog boxes. Therefore, in-process servers are most useful for utility functions.

The Fortran subroutine is declared in the Fortran code as follows:

SUBROUTINE FSTATS [DLLEXPORT](dSamp, dMean, dSD, dVar, lSize)

You may think that because the system creates the name FSTATS, it would be the name usable by external processes. However, this is not always the case.

There is no .DEF file created for this project, although one could be used, so the DLL developer has a bit less control over the actual name being exported. Does this make a difference?

For this compiler (and Visual C++ and some other compilers), the answer is yes. The name of the exported function is not always the same as that placed within the code unless you force the naming with a .DEF file. The user of the routine must know the name of the file so that the routine can be accessed. It is not guaranteed that the name you assign your function in the code will be the one exposed. You must also be aware that in the 32-bit programming environment all function names are case-sensitive. This can further complicate their access.

The program DUMPBIN.EXE (available in the Visual C++ compiler's BIN directory) helps resolve these issues. Use the command DUMPBIN /EXPORTS MEANSD.DLL to obtain the names of the exposed functions. The output of the program from the example DLL looks like this:

Microsoft (R) COFF Binary File Dumper Version 2.60.5135
Copyright (C) Microsoft Corp 1992-1995. All rights reserved.
Dump of file meansd.dll
File Type: DLL
 Section contains the following Exports for meansd.dll
 0 characteristics
 30201B0C time date stamp Wed Aug 02 20:40:44 1995
 0.00 version
 1 ordinal base
 2 number of functions
 2 number of names
 ordinal hint name
 1 0 _FSTATS@20 (00001010)
 2 1 _TEST@0 (00001000)
 Summary
 1000 .data
 1000 .idata
 1000 .rdata
 1000 .reloc
 1000 .text

Note that two things were exported: the FSTATS program and a test function that was thrown in to show this utility a bit better. The FSTATS program is exposed as _FSTATS@20, which indicates that the passed arguments take up 20 bytes of stack space, and the function _TEST@0 takes up no stack space (there are no arguments).

The Visual Basic 4.0 declaration statements must be modified to handle the changed names. For the FSTATS routine, this is handled as follows:

Public Declare Sub FSTATS Lib "h:\vboledll\MEANSD.DLL" _ 
 Alias "_FSTATS@20" _
 (ByRef dSamp As Double, _
 ByRef dMean As Double, _
 ByRef dSD As Double, _
 ByRef dVar As Double, _
 ByRef lSize As Long)

Note that with all the 4-byte addressing (five arguments all passed by reference), 20 bytes of stack space are taken up with the subroutine call. The sample array (dSamp) is passed in as a pointer to the first array element.

The in-process server is handled slightly differently from the out-of-process server. Initially, you should treat it as an in-process server for testing. This is because unlike an out-of-process server, an in-process server cannot be debugged at run time. Make sure the Use OLE DLL Restrictions box is checked in the Advanced Options tab of the Options dialog box. (You can check it even if you are running it as an out-of-process server.)

To invoke the project, follow the same steps as for the out-of-process server. The code in the calling routine follows. Note that the method is passed arguments.

Option Explicit
Dim Sample As Object
-------------------
Private Sub Command1_Click()
Dim dVars(100) As Double
Dim dSD As Double
Dim dVar As Double
Dim dMean As Double
Dim lNum As Long
dVars(1) = 100#
dVars(2) = 200#
dVars(3) = 300#
dVars(4) = 400
lNum = 4
'Load the sample data.
Sample.LoadData dVars(), lNum
'Recover the statistics.
Text1.Text = Sample.Mean
Text2.Text = Sample.StandardDev
End Sub
--------------------
Private Sub Form_Load()
'Create an instance of the object.
Set Sample = CreateObject("Statinproc.Sample")
End Sub
---------------------
Private Sub Form_Unload(Cancel As Integer)
'Ensure the object is cleaned up.
Set Sample = Nothing
End Sub

The code in the object is as follows:

Private dStandardDeviation As Double
Private dMean As Double
Private dVar As Double
------------------------
Property Get Mean() As Double
'Return the value of the mean.
Mean = dMean
End Property
------------------------
Property Get StandardDev() As Double
'Return the value of the standard deviation.
StandardDev = dStandardDeviation
End Property
------------------------
'The Method loads the data and calls the statistical function.
Public Sub LoadData(dArray() As Double, lSize As Long)
'Call the calculation function, saving the results 
'in the local copies of mean and standard deviation.
Call FSTATS(dArray(1), dMean, dStandardDeviation, dVar, lSize)
End Sub

In-Process vs. Out-of-Process Servers

Now that we have seen two kinds of OLE servers, the question is, what are the differences? Actually, the differences are quite fundamental. The following table shows the general issues:

Table 1. In-Process vs. Out-of-Process Servers

In-process servers Out-of-process servers
  Implemented as a DLL Implemented as an .EXE
Very fast interprocess communications Communications not as fast
Modal dialogs only No screen restrictions
Cannot be executed remotely Candidate for Remote Automation
Runs in memory space of each calling process Runs in its own memory space

Another significant issue is instancing. In-process servers can be instanced within each calling program, in effect enabling multiple instancing of the server without any potential conflicts. Out-of-process servers, on the other hand, run as single-thread applications. Each process calling an instance of the out-of-process server must share that resource, enabling only one to use it at a time. If the out-of-process server was created as a single-use object, this is not an issue because a new instance will be created for each instance of the object. A multiuse object does not generate a new instance each time it is "created" by a user. Because it runs as a single thread, it cannot be simultaneously used by two (or more) other processes.

The interprocess-communication speed between the servers is not a real issue if the implementation is done in a reasonable manner. If an out-of-process server is being used, arguments should be passed in a large group, using only one communications "burst." This would indicate that the out-of-process servers would likely make good containers for screens and auxiliary applications to a core program. In-process servers make good utility-program containers, as in database access layers and special functions for business-rules processing.

Other Issues

The process described in the preceding sections can be used by any language that can create a DLL, making Visual Basic 4.0 a language that can be the linkage mechanism to combine and simplify the interface to many languages. Visual Basic 4.0 can even combine the functions of several DLLs within one OLE object.

Other languages that are candidates for inclusion in Visual Basic 4.0 DLLs include COBOL, Pascal, and assembly language. The implementation techniques are virtually the same as the ones shown in this paper. It is recommended that if you want to use another language to create a DLL, examine the vendor's documentation carefully to ensure that you are using the correct options in the compile and linkage steps. The examples used here focus on the 32-bit world because those objects are the most flexible. They may be used in-process, out-of-process, and remotely.

A number of vendors make 32-bit compilers available for creating DLLs. When you are choosing a legacy compiler, make sure that the vendor supports DLL creation. The integration of that DLL into Visual Basic 4.0 is no different from what was described here.

Visual Basic 4.0 objects make access to code in other languages a trivial issue. Calling DLLs directly can be very complex—look at the Windows API declaration codes for some interesting syntax. Objects, however, are simple to implement and use. You can examine them through the Object Browser, for example, to learn which interfaces are available and thereby simplify development and make reuse a reality.

Distributing code to users

Visual Basic 4.0 handles the registration of objects within its environment. However, when you are distributing objects, you should register them on the target systems. The setup toolkit included with Visual Basic 4.0 provides just such functionality, as will numerous commercial alternatives. Objects do not automatically register (or unregister), so you should write appropriate code to handle the process. This task is a simple one and is thoroughly covered in the documentation for Visual Basic 4.0.

Remote objects

Now that an object can be created, consider the deployment of that object. Many application designs may specify that the OLE objects exist on the local computer, but Visual Basic 4.0 has a unique capacity to enable one to remotely execute out-of-process servers from another computer. The code actually executes on another computer while your process retains the freedom to do whatever it needs locally while waiting for results.

What this means is that any of the out-of-process servers you create in Visual Basic 4.0 (or for that matter any other language) can be called from Visual Basic 4.0 to execute on another computer. You can, for instance, have another computer call into a credit bureau to check a customer's credit while you continue to take an order. The remote routine can indicate when it finishes the credit check and pass you the results without the local computer wasting any CPU cycles. The tools to do this make the actual deployment a very simple matter. All the work lies in creation of the object, as described in this paper.

The architectural potential for this capability opens many options for application deployment. For example, business rules for a process can exist on a separately maintained server in order to keep the information contained there up-to-date. With Remote Automation, the deployment of updated modules containing business rules is a simple matter of changing the object on the server, rather than sending every user and potential user a new copy of the code. As a result, maintenance and administration costs are reduced.

In the context of legacy code, the OLE object and associated DLLs can exist on that remote computer. When the logic of the code changes for the system, it changes for every user of that system, resulting in nearly instantaneous system updates.

Included with The Enterprise Edition of Visual Basic 4.0 are tools that enable you to place the server on another computer. (Note that the host for the OLE server must be running Windows NT™ version 3.51 or Windows 95.) In fact, the tools set up the registry of each computer so the OLE routines are tricked into thinking that the process is on the local box. From the Visual Basic 4.0-oriented viewpoint, there is no difference; from a systems viewpoint, the difference is significant; and from the developer's viewpoint, all that must be done is to run one of the remote-administration tools, select the location of the server, and run the application.

The topic of remote servers is beyond the scope of this paper, but be assured that the hard part—the creation of the OLE object—is done for you here. The fun part—distributing that server—is a matter of copying some code to the server and a few clicks of a mouse.