by John Murdoch
Reprinted with permission from Visual Basic Programmer's Journal, Volume 8, Issue 11, Copyright 1998, Fawcette Technical Publications, Palo Alto, CA, USA. To subscribe, call 1-800-848-5523, 650-833-7100, visit www.vbpj.com, or visit The Development Exchange
Build this business process server as a model for your distributed apps.
How do you write an order-entry application? You might want to ask a question or two first, but you'll probably choose a Visual Basic client application running on a client machine, and a server database such as SQL Server running on a Windows NT server. Nice, neat, simple.
If you're an experienced developer, you've probably discovered that there are few nice, neat, simple order-entry applications—or, for that matter, customer-service applications, or export-shipping applications, or consumer-loan origination applications. Perhaps the application sells big-ticket items, so the client wants to pull a report from a credit-reporting agency. Perhaps the client sells hazardous materials, so the customer's license data must be checked with the U.S. Department of Commerce. Perhaps the application depends on submitted drawings or other images, so the order-entry process requires a fax server, an image repository, and some system to manage all those images. Perhaps your "nice, neat, simple" order-entry application requires them all. You might have client machines, one or more database servers, a fax server, and a server handling inbound and outbound modem traffic.
Once you finish that order-entry system, the client (or your employer) wants to incorporate customer service into the system, and perhaps manufacturing. And, while you're at it, perhaps they ask you to implement Electronic Data Interchange (EDI) links to the trucking companies. How can you build a reliable system that incorporates different programs running on different computers?
If you're using terminals connected to a single mainframe processor, you might design one huge application that handles everything. It would be enormous, and it probably wouldn't work at all until everything was finished. Maintaining this application would require several programmers with comprehensive knowledge of how the whole thing works. And changing it would be a nightmare.
Designing a series of small applications that communicate with one another is a much better plan. Instead of one massive system, you can write dozens of small applications, or components, that are distributed across the enterprise on a variety of machines. In this distributed scenario, some machines act purely as servers; others function both as servers and as clients of other servers. Still, other machines act only as clients. No one component is "the system." Instead, a distributed, enterprise solution is the sum of all the components across the enterprise. You can implement the system with a few components and expand it by adding components as they are written and tested. If the system changes, you only have to change the affected components. And, maintaining any one component doesn't require comprehensive knowledge of the rest of the system.
Microsoft's DCOM Is the AnswerMicrosoft's Distributed Component Object Model (DCOM) permits you to build complete distributed systems using components written with Microsoft Visual Basic, among other products. Chances are you're already familiar with Microsoft's Component Object Model (COM): You use it when you create a public instance of a class in an ActiveX server, and when you create an instance of that class from another application. DCOM permits you to create class instances from servers located on other machines—on a LAN server, across a WAN, or even across the Internet.
How can you use DCOM to build enterprise solutions? DCOM is an excellent tool when you want to:
Microsoft's documentation for DCOM, both in the VB5 Enterprise Edition and material available from its Web site, emphasizes using DCOM to manage pools of database server connections—particularly with Microsoft Transaction Server (MTS). Although that's a terrific way to handle a limited number of server connections, it's probably cheaper to upgrade your server license than write a lot of code to get around it. Managing pooled connections becomes useful only when you're managing hundreds (if not thousands) of connections on a single database server.
You can take advantage of DCOM in many more ways. For example, I'm doing a lot of work with imaging applications, letting a major furniture mill eliminate tons of paper from the factory floor. We use DCOM to connect order-entry workers with data, fax servers, and an AS/400. We use it to permit supervisors to monitor different stages of the order-entry process, and we use it to continuously monitor and display performance statistics.
As the project rolls out, we'll use DCOM to provide images to workers in shipping, customer service, and manufacturing. If you've dealt with complex business processes, you know that "where is the order?" is a question that comes up all the time. For this client, it's never an issue. Any department can retrieve the order—complete with diagrams, signatures, and special instructions—whenever it needs it.
Differences Between COM and DCOMA DCOM server, in theory, is identical to a COM (or OLE) server. You connect to it in the same way, you instantiate class objects in the same way, and your programs treat it the same way. In practice, however, there's a significant distinction: Practically anything a client program does with a DCOM server involves network traffic. Instantiating an object, calling a function, and assigning and reading property values are all network transmissions. (For more information, see the sidebar, "Before You Begin: Prerequisites to DCOM Programming.") Suppose your client application connects to a security server to log in, like this:
Before You Begin: Prerequisites to DCOM ProgrammingDCOM can be enormously useful, but you'll have to be prepared if your project is going to be successful. You must have the following:
Dim bAnswer As Boolean
Dim lLoginID As Long
Dim objLogin As WGSecurity.Login
' Network transmission #1
Set objLogin = New WGSecurity.Login
' Network transmission #2
With objLogin
.UserName = txtUser.Text
' Network transmission #3
.Password = txtPassword.Text
' Network transmission #4
bAnswer = .Login()
' Network transmission #5
If bAnswer = True Then
' Whoopee! We're In. . . .
lLoginID = .LoginID
' Network transmission #6
End If
End With
An in-process server or DLL on a client machine shares data with the client application at high speed. Yes, there's a performance hit, but the loss is so small it isn't worth worrying about. The performance hit is slightly higher when your application is connecting to an out-of-process COM server—but again, it isn't a big deal. When you're no longer dealing with an out-of-process server, but beginning to deal with an out-of-machine-and-across-the-network server, that performance hit can be more significant.
A good rule for large-scale computing: Keep traffic across the network to a bare minimum—you should bear this in mind as you design your components. If you tried Remote Automation with VB4 and found it horribly slow, you will be pleasantly surprised by DCOM. It's remarkably fast and entirely suitable for moving large amounts of data, such as huge TIF image files, to and from servers.
Using functions (exposed as methods) and passing a list of parameters is more efficient than using property procedures. You're still passing the same amount of data across the network, but you're sending it in one or two packets all at one time, instead of sending a blizzard of network packets. You might write the login procedure like this:
Dim bAnswer As Boolean
Dim lLoginID As Long
Dim sUser As String
Dim sPassword As String
Dim objLogin As WGSecurity.Login
' Network hit #1
sUser = txtUserName.Text
sPassword = txtPassword.Text
Set objLogin = New WGSecurity.clsLogin ' Network hit #2
bAnswer = objLogin.Login(sUser, _
sPassword, lLoginID) ' Hit #3
If bAnswer = True Then
' Whoopee! We got in!!!
Before you start building your DCOM server, you must decide whether you want to create a server for a specific project, or a server that can serve any number of projects (see the sidebar, "DCOM Server Design Questions"). On one hand, if you want to handle input/output functionality for an image repository, you probably want to write a general server that any number of client applications can call. On the other hand, if you're centralizing access to a device controller for a piece of capital equipment, you might be able to assume that one or two client applications at most will ever use your server.
DCOM Server Design QuestionsYou can use DCOM servers in many ways to build large-scale applications. Depending on how you want to use a DCOM server, you may want to permit multiple instances of a server to run—or you may not. You may want to write a general-purpose server, or you may require a specific-use server. Maybe you want to expose specific functions, or (as we do) limit the interface to a single function. As you plan your component-based project, consider these questions:
What form of instancing will you use?A server exposes one or more public classes based on class modules that you add to your project. You can set the Instancing property to permit multiple applications to connect to a single server. This is particularly handy when your server is monitoring the activities of a number of different users, such as in a workflow application. You can also set the Instancing property to force a new instance of your server for every application that invokes it. You'd do that if you're handling database connections, perhaps with Microsoft Transaction Server.
As you consider what Instancing option you'll use, bear in mind that single-use servers are launched and terminated as they are called. That is, when a client application creates a new instance of a single-use server, that server application is launched. If that single-use server makes database server connections at startup, there could be a noticeable performance hit while that startup process runs. If you find that this is a performance issue for you, consider implementing a pool manager, which keeps a certain number of single-use servers active and assigns them to client apps as they are needed.
As a rule, don't try to expose classes with different Instancing settings. If you have two classes, one that permits multiple-use and one that doesn't, your client applications may encounter an error, depending upon the order in which the classes are invoked. For example, client A creates a reference to the multiuse class, then the single-use class, and one instance of the server has been created. Client B creates a reference the multiuse class, and only one instance of the class has been created. Client C creates a reference to the multiuse class, then attempts to instantiate a reference to the single-use class. A second instance of the server is launched, and client C has references to the multiuse class in one server and the single-use class in another. If client C creates the single-use class reference first, both references will be to the second instance of the server. I cannot think of a circumstance where this kind of confusion would be a good thing.
A general-purpose server, or a specific-use server?A specific-use server lets you segment part of your application on a different machine. That may be useful for managing a modem pool or sharing some other device. A general-purpose server isn't part of any one specific project, it's written with the assumption that any number of client applications may make a connection to it.
How much of a separation of functions will you enforce?The accounting profession emphasizes a concept known as "separation of functions." The idea to is rigidly define job roles to prevent opportunities for mischief. In a similar sense, particularly with general-purpose servers, you'll want to make a distinction between the type of processing that you do in your DCOM server and the type of processing that you expect the client application to perform.
In "three-tier" development you make a distinction among the Presentation Tier (the part the user actually sees); the Business Rules Tier (where the rules of the business are enforced); and the Data Tier. Component-based application development blurs the "n-tier" concept somewhat by allowing for more complex relationships between business rules and application data. What sort of distinctions will you make in your design and how rigidly will you enforce those distinctions?
In a general-purpose server, which is what we use most often, we avoid doing any form of formatting or manipulation of data. In the GetData() function shown in the main body of this article, for instance, note that the procedure doesn't test whether the SQL statement is valid or if any rows are returned. The server is written with the assumption that the client app programmer knows what she's asking for and will act responsibly with what she is given. We have found that general-purpose servers require a very strict separation of functions.
We view a specific-use server as part of a particular application, so a programmer—in theory--doesn't have to worry about separation of functions. In practice we tend to be just as maniacal about separation of functions in specific-use servers because we have frequently seen servers originally written as specific-use servers become general-purpose servers over time. The client realizes what the server does, which suggests another application that can take advantage of the server.
We make a semantic distinction between a specific-use server, which includes functionality defined with a specific client application in mind, and a general-purpose server, which is written strictly to provide defined services to any client application that calls. If you're writing a specific-use server, you might want to expose a list of methods with strongly typed parameters. This approach is similar to the coding practices you and I use within a VB project—we separate different logic into specific functions and call a specific procedure when we want it. This approach partitions the logic between the client and the server. You might format the data at the server to facilitate its display on the client, for instance, or to structure how the data is returned to include precalculated values, such as the sum of a price resultset.
We almost always write general-purpose servers. We've discovered that clients prefer component-based development once a project is installed. As soon as the client sees the benefits, many new features are added to the project in subsequent revisions. When we exposed a large number of methods with strongly typed parameters, we frequently had to change parameters, which often meant recompiling and reinstalling existing client applications. Nowadays, we consciously work to use a limited number of general methods with generalized parameters:
Public Function _
SendMessage(lMessageType As Long, _
vKeys As Variant, vData _
As Variant, _
vExceptions As Variant) As Boolean
When the DCOM server method is invoked, we use a Select Case statement to determine how to handle the call (we refer to this method's parameter list as a "message"):
Select Case lMessageType
Case glLOGIN
bAnswer = Login(vKeys, vData, _
vExceptions)
Case glGET_DATA
bAnswer = GetData(vKeys, vData, _
vExceptions)
Case glGET_SP_DATA
bAnswer = GetSPData(vKeys, _
vData, vExceptions)
Case glPUT_DATA
bAnswer = PutData(vKeys, vData, _
vExceptions)
Case Else
gobjMessageLog. _
LogMessage(glUNKNOWN_MESSAGE)
bAnswer = False
End Select
SendMessage = bAnswer
The benefit of this approach is that the DCOM server's public interface never changes—we don't have to worry about backward compatibility with existing client applications. Note a couple drawbacks, though: The client application must send a valid message number, and the variants in the message (which typically are arrays) must be structured correctly. We handle this by including a common file of constants in each client project. The message numbers are defined as constants, and we carefully validate each message in the server procedures and the return values in the client procedures.
Try to avoid specific functions, such as GetCustomerData() or GetOpenItems(). Instead, code general functions such as GetData() (see Listing 1).
Listing 1
Dim bAnswer As Boolean
Dim lLoginID As Long
Dim objLogin As WGSecurity.Login
' Network transmission #1
Set objLogin = New WGSecurity.Login
' Network transmission #2
With objLogin
.UserName = txtUser.Text ' Network transmission #3
.Password = txtPassword.Text ' Network transmission #4
bAnswer = .Login() ' Network transmission #5
If bAnswer = True Then
' Whoopee! We're In. . . .
lLoginID = .LoginID ' Network transmission #6
End If
End With
Listing 1: Keep it Generic. Code general functions such as GetData(), rather than specific functions such as GetCustomerData() or GetOpenItems(). Any client application can use this code to execute a SELECT statement and return a recordset (formatted as an array).
Note how generic this code is. Any client application can use it to execute a SELECT statement and return a recordset (formatted as an array). We have similar procedures to retrieve recordsets from stored procedures, insert data with INSERT INTO statements, and change records with UPDATE statements. In a general-purpose server, we don't have procedures to perform specific functions for specific client applications—or at least we try hard not to.
I mentioned earlier that we refer to the list of parameters passed to the SendMessage() function as a "message." This message has three parts: keys, data, and exceptions. We define keys as the necessary data the server will require: It might be a SQL statement, a list of data elements, an image file to store, or something else; it is data sent from the client to the server. We define the data part of the message as the (technical term coming) stuff sent back from the server to the client. What is an exception? That's the term we use for any kind of error that occurs on the server.
Why not use the word "error"? You and I write code to handle errors all the time—we understand an error to be a circumstance where the program fails to execute properly. What do you call a circumstance where the program functions correctly, but it does not do what the user intended? Semantics? Not really—you have to make a distinction in your server, and in your client applications, between actual program errors ("Object not found in this collection") and those times when a program is uncooperative (such as rejecting a login with the wrong password). We always expect the vExceptions variant to be an array, with the error number, error description, module, procedure, and indicator of severity. Actual program errors are reported to the user immediately, but other problems might not require informing the user or might even be intentional (such as when we're checking whether a file on a remote server exists).
Building a Business Process ServerTo help you understand how to use DCOM in your applications, let's build a business process server. This server accepts a variety of information about a customer—whether it's a new customer or an existing one—and will determine whether the customer's credit profile is acceptable. If a new credit report is needed, the server retrieves it. The customer's credit information is scored using an algorithm—the higher the score, the better the customer's credit rating. (See the sidebar, "10 Tips on Building DCOM Servers.")
10 Tips on Building DCOM Servers1) First, Determine, whether this server will only be used among clients of a specific application (that is, multiple instances of a single EXE), or among clients running different EXEs. This is an important design consideration, and changing your mind later may cause significant changes in your server and in the dependent client applications.
2) Avoid using property procedures. Anytime you assign a property value or read a property value, your client application is creating network traffic.
3) Use public methods with parameter lists to cut down on network traffic. We typically expose a single method in each server:
Public Function SendMessage(lMessageType As Long, vKeys As Variant, vData As Variant, vExceptions As Variant) As Boolean
4) Whenever you add functionality, use a few generalized methods, rather than revising your DCOM server and risking changing the version information.
5) Don't use Collection objects in parameter lists. Use arrays instead. Remember that a collection is effectively an array of object references, and those references refer to objects on the client machine. The references will be meaningless to a DCOM server on a different machine.
6) Carefully consider how you'll handle program errors, such as "file not found," and program exceptions, where a component returns an answer that the client application may not like. How will you provide information for debugging and maintenance?
7) Consider how your DCOM server will be instantiated. Will your server permit more than one connection or will a new instance of the EXE be launched when another app calls? If multiple users can connect to the same object, pay careful attention to re-entrancy problems. What will happen if two, three, 12, or 20 client applications all call the same function at the same time?
8) If you're creating single-instance DCOM servers, consider the scalability implications. What will happen if 50, 80, or 300 instances of this server are launched by client applications? You might want to include Microsoft Transaction Server (or another pool manager) in your plans.
9) Test your new server on a client machine first. Get all the bugs out before you install it as a DCOM server.
10) Document—relentlessly—all the ways your server can be used. You are effectively publishing an application programming interface (API).
This kind of server is enormously useful in commercial applications, both for qualifying new customers and periodically reviewing the credit profiles of existing accounts. If you've recently purchased a car, for instance, the interest rate you pay on your car loan might depend upon your credit score. You might use this server from a loan-origination application, a credit-review application, or even a marketing application. It's an excellent choice for a general-purpose DCOM server that can be shared by different applications across the network. (Be sure to read the sidebar, "Caution: For Educational Use Only.")
Caution: For Educational Use OnlyThe credit-scoring server you'll create here is for demonstration purposes only. Credit-scoring algorithms must be carefully prepared to be fair to all applicants. This application is intended to demonstrate a coding technique, not provide a utility you can plug into your current project tomorrow.
Even though the algorithm is not for commercial application, this server can be useful. You can install it for testing purposes to make sure that your network, servers, and client machines are set up properly for DCOM. You can download the project from the Development Exchange at www.devx.com (see "Code Online" for details). It makes sense to build a simple test project or just use this one as a model. You can let the network administrators handle the details of installing and configuring DCOM, while you build your components. You don't want to wait until deployment to discover that the network doesn't support TCP/IP.
The first step in creating your DCOM server is to open a new project. VB5, regardless of which Service Pack you install, doesn't include a template for DCOM Server. The template you want to use is ActiveX EXE. (You can also build DCOM servers as ActiveX DLLs and use the surrogate process on the server.) When the new project is created, Visual Basic automatically adds a class module. We typically expose only one public class in a server. As I mentioned earlier, we've learned from experience to limit the interface to a single method. We almost never include forms with a DCOM server—there's no need for a user interface.
So where do you begin? Many VB developers make a practice of having projects start from Sub Main. That's a sound practice, and one we enforce—except with COM servers. The COM server always starts with the Class_Initialize event:
Private Sub Class_Initialize()
' Version: 1.0.0
' Date: 6/19/1998
' Coder: Murdoch
' Action: Initial keyin
' Purpose:
' When class is init'd, start logging
' events to a log file. Note that
' we log the events to a text file,
' rather than the NT event log.
' =============
' Declarations:
Dim sFile As String
Const vbLogToFile As Long = 2
' Def'd as intrinsic in VB5.HLP, but
' not actually there...
With App
sFile = .Path & "\WGSCORE.ERR"
.StartLogging sFile, vbLogToFile
End With
End Sub
This example is quite simple—all we're doing is logging events to a file. This code segment runs each time this class is initialized. If four users currently have references to this DCOM server, this class is initialized four times—and the App.StartLogging command is also issued four times. If you want your DCOM server to share a scarce resource (such as a global database connection), you might want to ensure some parts of your code run only when the DCOM server is started—when a class reference is made by the first client application (see Listing 2).
Listing 2
Select Case lMessageType
Case glLOGIN
bAnswer = Login(vKeys, vData, vExceptions)
Case glGET_DATA
bAnswer = GetData(vKeys, vData, vExceptions)
Case glGET_SP_DATA
bAnswer = GetSPData(vKeys, vData, vExceptions)
Case glPUT_DATA
bAnswer = PutData(vKeys, vData, vExceptions)
Case Else
gobjMessageLog.LogMessage(glUNKNOWN_MESSAGE)
bAnswer = False
End Select
SendMessage = bAnswer
Listing 2: Wait for the Start. If you want your DCOM server to share a scarce resource, you might want to ensure some parts of your code run only when the DCOM server is started—that is, when a class reference is made by the first client application.
You can get quite sophisticated in how you do this. For example, we have a current project that maintains a collection of class instances—one for each connected user. We can use that collection to display performance statistics to supervisors, for instance, and to track user software licenses for some expensive components. (If you want to do something similar, keep an accurate count of connected users, particularly if you need to close a database connection or perform some other shutdown procedure when the last connection to your DCOM server is dropped.)
If you have created the necessary code to run when the component is initialized, handle messages to and from your server, and run when your component terminates, you're ready to compile. This might seem awkward for a VB project, particularly if you're used to drawing all the visual stuff before you write any code. There's no Visual, just Basic.
The code segments listed in this article all come from the VBPJ_Score and VBPJ_Score_Client projects, which you can download from The Development Exchange at http://www.devx.com (see the Code Online box for details). You can use both projects as "proof of concept" code—installing them correctly connects them across your network using DCOM. But you can also open the source code for each project (in a different session of Visual Basic) and see how the client and server interact.
Compiling Your ProjectWhen you're ready to compile your project, make sure Visual Basic knows to compile it as a DCOM server. Select the Properties item from the Project menu in the VB IDE. The Compile tab lets you choose whether to compile to p-code or machine code. You can create a DCOM server that's compiled to p-code, but we always compile our servers to machine code because it runs faster. The Component tab lets you define how this component will be launched (select ActiveX Component) and whether this is a Remote Server project (see Figure 1). You must check the Remote Server box; otherwise, your component can be used only as an out-of-process server on the same machine as the client application. When you check the Remote Server box, a file with a VBR extension is created during compilation. This file, which will be included in the client application install set, is a key part of making the DCOM connection.
Figure 1: The Component Tab of the VBPJ_Score Project Property Sheet. You must check the box to produce Remote Server files, or you can't use this as a DCOM server.
You can also specify compatibility with another project, which you must do immediately after you compile the project for the first time. DCOM depends on the identification of your server component by its class ID. If the IDs change each time you compile, your client applications won't recognize a new build of the DCOM server.
Ready to install your new server and fire up the entire system? Not so fast. Before you think about distributing your DCOM server, you have a lot of testing to do. Testing is absolutely crucial. Debugging an ActiveX server, especially a DCOM server, while attempting to build a client app is a colossal exercise in frustration.
Each DCOM server I build includes a test function. I use it just to prove I can connect to the server and pass a message to my public interface. I begin the testing process even before I compile the project. Our standard server design is so simple that I can test it simply by running the project in the development environment and typing commands in the Debug window. As I add a function to the server, I test it with the Debug window. I create an instance of the public class, assemble the variables passed as the message, and call the public function. I set breakpoints in the code, make any necessary changes, and make sure everything works.
Once you complete your initial testing, you might want to consider automated testing through tools such as Visual Test (originally from Microsoft, but now available from Rational Corp.) or SQA Suite (also from Rational). You might also want to use test data packages, such as TestBytes from Logic Works Inc.
Creating Your Server Install SetThe Application Setup Wizard that ships with VB5 creates an install set for DCOM servers. Run the wizard as you usually do for any VB project. The wizard prompts you for instructions about whether the application will be installed as a standalone application or as an ActiveX component. You'll also be asked whether the server will be accessed by Remote Automation, which is an older, slower, clunkier technology that shipped with VB4 (see Figure 2).
Figure 2: The Application Setup Wizard. The wizard automatically detects that you're creating an install set for an ActiveX Server. Select the options shown here.
Select the option to install the application as an ActiveX component. You should also select the No option to the question of whether this component will be used with Remote Automation. You're using DCOM, not Remote Automation. After you create your install set, you might be tempted to delete the compiled EXE from your machine—don't do it. You need that to ensure project or binary compatibility with your source code. You also need it when you test your client connections (which I'll cover soon).
Installing the server itself is no different from installing any other VB application. We use the Application Setup Wizard provided with VB5, but you can use any third-party installation toolkit. The critical part of installing a DCOM server is making sure the server permits DCOM connections to your application. To do this, run the DCOMCNFG.exe application on the server. DCOMCNFG.exe is located in the System32 subdirectory under your Windows NT directory. You'll find DCOMCNFG.exe ready to use on either Windows NT 4.0 Workstation or Windows NT 4.0 Server. (See the sidebar, "Using Windows 95/98 as a DCOM Server.")
Using Windows 95/98 As A DCOM ServerYou can also use Windows 95 or 98 as a DCOM server, but you'll have to download additional files from Microsoft's Web site to do so. Retrieve the self-extracting archive file DCM95CFG.EXE and directions from www.microsoft.com/com. The Windows 95 version of DCOMCNFG.EXE will be in your Windows System directory. It's similar to the version on Windows NT, described here, but you have fewer security options to fill out. While it's possible to use a Windows 95 machine as a DCOM server, I don't think it is a good idea. Windows 95 is a great client system, but the DCOM servers you'll create are the heart of your enterprise-development project. Spend the extra money to install Windows NT 4.0.
The first step in setting up DCOM on the server machine is to create a Server User. DCOM offers different security settings: Your DCOM server can run with the permissions of the launching user, the accessing user, or a specified user. The launching user launches the DCOM server; the accessing user makes the current connection; and the specified-user setting permits you to identify a specific user account whose permissions the DCOM server uses.
We always specify the user. This permits us to completely secure the data on the server. The Server User typically has administrative privileges (because servers usually end up in the WinNT directory, which we usually restrict to administrators), although you might want to restrict the rights a bit more. You don't have to specify a user to use DCOM, but we've found this offers us and our clients tighter security. The next step is to run DCOMCNFG.exe. When you launch DCOMCNFG.exe, it displays the class IDs of all the COM servers installed on the server. Locate the server you're looking for, and select it (see Figure 3).
Figure 3: The DCOMCNFG.EXE Utility. Select your DCOM server and click on the Properties button.
The Properties page of DCOMCNFG.exe contains four tabs: General, Location, Security, and Identity. The General tab provides information about the server, but has no changeable settings. The Location tab lets you specify whether the server will run on the machine where it is installed or on another machine (see Figure 4). Because the point of DCOM is to run the server on the server machine, and not the client, make sure the option to run on the server machine is the only option checked.
Figure 4: The Location Tab of the Property Sheet. Make sure you only have the check box for "This Machine" checked.
The Security tab lets you specify which users and groups can access, launch, and configure the server (see Figure 5). If you've created a single-use server, the launch-user-versus-access-user distinction is meaningless—you can't access the server if you can't launch it. If you have a multiple-use server, though, you might want to restrict launch permissions to further enhance security. One installation of ours has a multiple-use server launched only by an NT service that launches at startup. If that service isn't running, the DCOM server can't be launched, and no one can connect, regardless of their access permissions.
Figure 5: The Security Tab of the Property Sheet. Set the option buttons to custom configure the settings, then click on each of the buttons to specify which users have which permissions.
Set the option buttons to customize the permission settings you require. When you click on the button(s), you see the standard Windows NT dialog for adding users or groups. The default display shows groups, but you can click on the "Show Users" button to see a list of users as well.
Add users or groups in accordance with your local security requirements. I've included several users to illustrate that you can add specific users, but we usually group application users together and just add the group or groups (see Figure 6).
Figure 6: The User/Group Security Dialog. This instance shows users and groups that have launch permissions, but all of the security dialogs are identical.
The last tab of the Property sheet, the Identity tab, lets you specify the security permissions the DCOM server will have (see Figure 7). When your client-side application launches the DCOM server, the user of that application doesn't need to be logged into the server machine. That's why you must specify who can launch and access the server. When the DCOM server runs, whose rights does it use? You can configure the server to run with the permissions of the launching or accessing user, or you can use the permissions of a specified user. As I mentioned earlier, we always specify the user—we like to prevent users from accessing the machine in any way except through the DCOM server.
Figure 7: The Identity Tab of the Property Sheet. We prefer to specify a user account created especially for DCOM servers.
Testing Your ConnectionsBefore you install your client application all over the network, you first have to test whether you can connect to the DCOM server. The best way to do this: Use the Remote Automation Connection (RAC) Manager, a utility that ships with VB5 (see Figure 8). Despite the name, you can use the RAC Manager to test DCOM connections as well as Remote Access. Remote Automation was an attempt at intermachine communication that shipped with VB4. Although it held promise, it was horribly slow. I don't recommend using it—use DCOM instead. The RAC Manager was installed when you installed VB 5.0. Launching the RAC Manager from your start menu displays a list of COM objects on your computer.
Figure 8: The Remote Automation Connection Manager. This dialog indicates that the VBPJ_Score.clsScore object will be found (through DCOM) on the server machine named "NJ."
Select the classes exposed by your server application. You'll see each of the public classes in the RAC Manager. Enter the name of the server (don't include backslashes) in the Network Address field and choose Distributed COM as your Remote Transport option. Choose Remote from the Register menu to save your entry. (You can also press CTRL+R or right-click on the dialog and choose Remote.) If you have more than one class, you can select all your classes at once, enter the server address, and press CTRL+R.
The RAC Manager can come in handy. You can use it to change your server settings from your local machine to the server (or to different servers) as often as you choose. I program with a notebook computer, so I bring my development machine with me when I visit client sites. If I need to test something, or run a new version of a client application, I can use the RAC Manager to connect to the DCOM servers at that particular site. However, the RAC Manager is not a redistributable component—you can't simply install it on each client machine. To make the DCOM connection from a client machine, you have to include DCOM instructions in your install set.
The Application Setup Wizard that ships with VB5 permits you to create install sets for applications that connect to DCOM servers (see Figure 9). Run the wizard as you usually do, selecting your client application project file and proceeding from there. As you progress through the wizard, it automatically identifies client app dependencies, including a dependency for your DCOM server.
Figure 9: The Application Setup Wizard. The wizard has detected that our client app depends upon an OLE server. Note that I have unchecked the box for the server on my machine and added a reference to the remote server information file (the .VBR file produced when we compiled the DCOM server).
The Application Setup Wizard automatically identifies the local copy of your DCOM server (the version you compiled and included in your server install set). Uncheck that box! You don't want to ship the DCOM server EXE in your client application install set. Instead, you want to ship information about your remote server. Click on the button labeled Add Remote.
You see a common dialog asking for the VBR file. The VBR file contains the remote server information. It will be in the same directory as the server EXE you compiled earlier. Here, as with the RAC Manager, select Distributed COM as your Remote Transport protocol, and enter the server name without backslashes. Note that the Network Protocol and Authentication options are disabled—those are only used with Remote Automation (see Figure 10). Must you define the server when you build the install set? The short answer is "yes." If you can't deal with that, you have to write your own application to shell out to CliReg32.exe to register the DCOM server dependency.
Figure 10: The Dialog Box to Identify Remote Servers. Enter the server name (no backslashes) and select DCOM as your Remote Transport protocol.
Installing the ClientInstalling your client application on a Windows NT Workstation machine is no different from any other kind of installation. Just run the Setup wizard. When the Setup wizard is done, the client application should be able to connect to your DCOM server.
If your client machines have Windows 95 installed, you have to install DCOM95. You can download DCOM95 from Microsoft's Web site at http://www.microsoft.com/com; follow the hyperlinks from there. DCOM95 is automatically installed with Internet Explorer 4.0 and is included in Windows 98. Run DCOM95.exe, a self-extracting archive, on the client machine. It will tell you when it has successfully installed.
Once you install DCOM95, you can run the Setup Wizard for your client application. When Setup has successfully completed, you can connect to the DCOM server. Right before you run the first client application at a new site, consider sacrificing a young network administrator to make sure things run smoothly. If you do everything correctly, the client application should make the connection. If the client app doesn't connect to the DCOM server (did you sacrifice that network administrator?), check several things.
First, identify the error message the client application reports. Typically, we see Error 7 and Error 429. Error 7 is actually good news: The error description says "out of memory," but it indicates you've made the DCOM connection. However, your DCOM server doesn't have adequate rights on the server machine. Smack your forehead (or the nearest surviving LAN administrator) and go change the server user's rights.
The other possibility is Error 429, "OLE server cannot create object." This error can arise from one of several things:
The source code for this project includes complete install sets you can use. The install set for the server (VBPJ_Score) doesn't need to be changed—you can run that on your server. You have to rebuild the client install set to refer to your server rather than mine, but otherwise you can install that project, too. As I mentioned earlier, it makes sense to install the demo applications early in the process, to prove you can make a DCOM connection. Also check out the sidebar, "Documenting Your Project."
Documenting Your ProjectComponent-based application development permits teams of developers to build large systems. Some programmers will develop tools (DCOM servers, for example), while others design the user interface or handle database tasks. None of the programmers will get far if the DCOM servers aren't thoroughly documented. We make a practice of documenting every single function supported by a DCOM server. That's part of the cost of the single public-interface design approach we take, and we're willing to deal with it. Fortunately, my clientele is willing to foot the cost of that documentation.
You might think documentation is a pain; you might feel the project budget doesn't include funding for writing down instructions; you might feel that documentation makes you expendable. I am utterly unsympathetic. What's the crisis besetting corporate IT in the U.S. and elsewhere? The Year 2000 Crisis. What's at the root of that? A Fortune 500 IT manager I know frequently insists that it should be known as the "Undocumented Mainframe Job Crisis" instead. If the undocumented mainframe app gets replaced by an undocumented component-based app, how have you improved that company's infrastructure?
I confess—I'm a sucker for network integration schemes. I built projects using store-and-forward systems, projects with NetDDE, and projects with Remote Automation. I loved them all. DCOM, by contrast, really works. You might find making your first connection a frustrating process, but once you've done it, the process is simple the second time around. And once DCOM is working, it doesn't break. I have log data from DCOM servers that have been handling large transaction rates, with image files typically 28K in size in each transaction, for eight weeks and longer without interruption. It's remarkably fast and scales to large numbers of users. If you design your application carefully, you'll see small network usage, and the system usage on the server machine will be trivial. We see usage ar less than 5 percent at peak transaction time.
Corporate IT still sees computers as high-speed cash registers. You and I write order-entry apps, accounts-payable apps, general-ledger apps, and so forth. If the client is trying to be au courant, what are you writing? An order-entry app with an Active Server Page as the front end. What's the hot application taking corporate America by storm? ERP applications, such as SAP R/3, which essentially are Fortune 500 versions (with Fortune 500 price tags) of Intuit's QuickBooks.
That will change. Companies are changing how they do business, and the IT department isn't keeping pace. A modern auto plant doesn't want to know how many parts it has in inventory—it doesn't keep inventory. It depends on suppliers shipping presequenced parts that arrive within hours of being installed on the assembly line. The plant wants to know when the supplier shipped it, what boxcar it was shipped on, and when it will arrive. That means integrating the systems of the auto plant, the supplier, and the railroads. You and I can do that kind of integration work using DCOM across TCP/IP network connections. We can extend the reach of our systems beyond where the mainframe apps have gone, to build a whole new class of enterprise applications.
Years ago, right after BasicPro magazine was renamed to VBPJ, VBPJ publisher Jim Fawcette wrote an editorial commenting on Microsoft's announced vision of a computing world based on small, reusable components that could be linked together using VB. On one hand, he wrote, it seemed like a stretch to see how this vision would take shape—rewriting Excel as a VB app seemed kind of pointless. On the other hand, he wrote, if this stuff worked, it would mean guaranteed lifetime employment for VB programmers. This stuff is DCOM—and it really works. n
John Murdoch is president of Wind Gap Technology Group, in Wind Gap, Pa. He has written extensively about Visual Basic and large-scale application design, and has been a technical contributor to the Microsoft Certified Professional program for Visual Basic. He designs client/server projects for corporate clients in North America and Asia.
Download the code for this article here