Previous Page TOC Next Page



– 23 –
Creating and Managing Remote Automation Objects


Remote Automation is the method employed by Visual Basic 4.0 to permit OLE Automation client applications to access out-of-process OLE Automation servers over a network connection. Prior to the introduction of Visual Basic 4.0's Remote Automation features, in-process and out-of-process OLE Automation servers were required to reside on the same PC as the OLE Automation client application. Remote Automation makes implementation of distributed three-tier client/server database applications possible.

Remote Automation is an interim technology. When Visual Basic 4.0 was released, Microsoft unofficially called Remote Automation "NetworkOLE 0.9." When this edition was written, Microsoft had abandoned NetworkOLE terminology in favor of the Distributed Common Object Model (DCOM). DCOM, which is expected to be included in Windows NT 4.0, also called the Shell Update Release (SUR), offers many improvements to the Remote Automation methodology of Visual Basic 4.0. In particular, DCOM is slated to provide substantially improved security features and more efficient Remote Procedure Call (RPC) transport than Remote Automation. DCOM is expected to play a major role in making corporate intranets faster and more efficient. Fortunately, the out-of-process OLE servers you write for use with Visual Basic 4.0's Remote Automation features are operable in the DCOM environment with little or no modification.

This chapter describes the theory of Remote Automation Objects (RAOs); where RAOs are most useful; and how to write and deploy RAOs on Windows NT Server 3.51+, and on Windows 3.1+, Windows 95, and Windows NT Workstation 3.51+ clients. The chapter also describes how to use the utilities included with the Enterprise Edition of Visual Basic 4.0 for registering, managing, and cataloging RAOs.

Understanding Remote Automation


Remote Automation relies on a Remote Automation proxy and Remote Automation stub to provide network communication between the OLE Automation client and server. The fact that the OLE Automation server is "remoted" is transparent to the client. The Remote Automation proxy takes the place of the OLE proxy that's used by local OLE Automation clients to communicate by Lightweight Remote Procedure Calls (LRPCs). Figure 23.1 compares the conventional OLE proxy and OLE stub structure for local servers with the architecture of RAOs.

Figure 23.1. A comparison of conventional local OLE Automation Server (top) and Remote Automation Object architecture (bottom).

The following two sections describe the individual components that implement the Visual Basic 4.0 version of Remote Automation and the alternatives for operational implementation of RAOs.

Remote Automation Components


Visual Basic 4.0 supports 16-bit Remote Automation clients running under Windows 3.1+, 16-bit or 32-bit clients on Windows 95 and Windows NT, and 32-bit Remote Automation servers on Windows 95 and Windows NT. Following is a list of the basic components that make up Visual Basic 4.0's current (pre-DCOM) implementation Remote Automation:

Figure 23.2 illustrates three different Remote Automation client configurations operable with a single Remote Automation server. RACMGR16.EXE and Racmgr32.exe appear in parentheses in the client elements of Figure 23.2 because Remote Automation Connection Managers are options for Remote Automation clients. It is unlikely that Windows 95 will find use as a production Remote Automation server; Windows 95 is included in Figure 23.2 as a Remote Automation server option only for completeness. (If you don't have Windows NT Server 3.51+, you can test Remote Automation examples with networked Windows 95 PCs.)

Figure 23.2. Typical client configurations and the standard server configuration for implementing Remote Automation.

Synchronous Versus Asynchronous Server Operation


The conventional method of interaction between Remote Automation clients and servers is synchronous. In synchronous mode, the client/server interaction occurs as follows:

  1. The Remote Automation client creates an instance of the RAO on the Remote Automation server by executing a Dim objName As New ObjType statement.

  2. Optionally, the client passes property values to the new ObjType instance on the server.

  3. The client applies a method of ObjType, usually with property values passed as arguments of the method. (As noted in Chapter 16, "Creating Local OLE Automation Servers and DLLs," setting property values with method argument values is faster than passing property values to Property Let procedures.

  4. After applying the method, client processing halts and server processing starts. If the server type is Creatable SingleUse, processing for other clients must be completed prior to server processing for the new client, unless multiple Creatable SingleUse server objects are provided by a server pool manager.

  5. During server processing, communication between the client and the server is limited to passing "keep-alive" tokens, if necessary.

  6. When the server completes its processing, it returns control to the client, which then continues executing its code. The instance of the server remains open so that the client can query property values exposed by the server's Property Get procedures. (For better performance, property values are returned as values of method arguments.)

  7. After finishing with the server, the client explicitly unloads the server instance with a Set objName = Nothing statement. Until the client unloads the server, keep-alive tokens periodically pass between the server and client.

Synchronous mode duplicates the process by which local servers communicate with OLE Automation clients. Thus, you can "remote" a local server without making any changes to the server code.



Do not include visible user interface objects, such as dialogs or message boxes in Remote Automation servers. Such visible objects appear on the server's display, not on the client's. To create reasonably bulletproof RAOs, all runtime errors must be trapped and you must implement a means to communicate error information to (and handle error processing in) your client application.

Asynchronous mode lets the server process requests while the client continues to execute code. One of the complaints of Visual Basic developers is the inability of Visual Basic to handle Windows API callback functions. Remote Automation, however, lets a client pass a callback function to the server before applying an object method to start server processing. The server accepts the property values or method argument values and then returns control to the client. Asynchronous mode is especially useful with Remote Automation servers that perform large-scale database tasks, such as acquiring and relating data from multiple data services. Asynchronous Remote Automation mode follows this route:

  1. The Remote Automation client creates an instance of the RAO on the Remote Automation server by executing a Dim objName As New ObjType statement.

  2. The client passes a property value to the new ObjType instance on the server that refers to the client object. The server uses the client self-reference to perform the callback operation.

  3. The client applies a method of ObjType, usually with other property values passed as arguments of the method.

  4. After applying the method, client processing halts and server processing of a "wake-up" mechanism starts. (In database applications, the wake-up mechanism usually is completion of a database query.)

  5. The server exits the method function, returning control to the client application. (Because control is returned immediately to the client, you can't pass property values to the client as values of calling method arguments.)

  6. The client continues executing its code, such as performing other database operations.

  7. When completing its processing of the wakeup process and any subsequent operations, the server applies a method to the object passed by the client.

  8. Server processing halts, control is returned to the client, and the client performs required operations, such as gathering data from the server.

  9. The client exits the callback function, allowing the server to continue code execution. The server continues to exist and the client can interrogate the server to obtain property values.

  10. After finishing with the server, the client explicitly unloads the server instance with a Set objName = Nothing statement.

Asynchronous mode involves more complex code and eliminates the flexibility of passing property values as arguments of the calling method. However, you can pass property values as values of arguments of the callback method. If your client application can accomplish useful work during the server processing interval, use of asynchronous mode usually is worth the additional code and testing effort. Otherwise, the more straightforward synchronous mode is adequate.

Defining the Role of Remote Automation in Database Applications


The majority of Visual Basic 4.0 Remote Automation applications involve databases. This isn't a surprising observation because the majority of all Visual Basic applications involve databases. The "Defining the Role of OLE Automation Servers" section of Chapter 16 describes the basic reasons for using OLE Automation servers in database applications. Following are the primary advantages of moving from local to Remote Automation servers:



You must have at least as many licenses for Access 95 as the number of simultaneous instances of clients using Access 95 on the server. There are a variety of licensing terms for server-based Microsoft Office 95 components. Consult your license agreement to determine licensing requirements for Access 95 or other Office 95 applications running on servers.

The sections that follow describe the two most commonly used architectures for Remote Automation operations.

Conventional Two-Tier Database Applications


The simplest implementation of Remote Automation is a two-tier architecture in which the RAOs and the RDBMS reside on the same remote PC. Typically, two-tier architecture might be used for online transaction processing (OLTP) with a Jet 3.0 .mdb database located on the same server as the RAOs. Another application for two-tier architecture, illustrated by Figure 23.3, is use of a remote copy of Access 95 for printing reports, as described in the preceding section. In either case, a copy of Jet 3.0 must reside on the server.

Figure 23.3. Two-tier Remote Automation architecture for printing reports with a server-based copy of Access 95.

Three-Tier Database Architecture


True three-tier architecture requires that the RAOs be installed on an application server that is separate from the application server(s) for the database(s) accessed by the RAO. The primary advantages of three-tier architecture are as follows.

Figure 23.4 is an illustration of three-tier client/server architecture with user services (clients), business services (RAOs that connect to the Jet 3.0 DAO and Visual Basic's RDO 1.0, both of which use 32-bit ODBC drivers), and data services provided by a heterogeneous set of client/server and mainframe RDBMSs that support ODBC, either directly or through a database gateway.

Figure 23.4. Three-tier architecture for distributed, heterogeneous client/server and mainframe RDBMs.

Designing a Multi-Tier Remote Automation Application


Order entry is one of the most common OLTP database front-end applications and is a prime candidate for Remote Automation. Sophisticated order entry front-ends are likely to involve complex business services, such as the following:

Order entry front-ends that require extensive business services often must connect to multiple databases. Customer billing and accounts receivable data is likely to be stored in a financial database, inventory data in a warehouse database, and production or vendor delivery schedules in a manufacturing database. Thus, a three-tier architecture that abstracts the necessary order entry data from multiple databases is ideally suited for complex order processing systems.

Remote Automation's asynchronous mode is especially effective for telephone order entry systems. As an example, when the order entry operator identifies the customer, a remote credit check object can be used to determine payment history and credit limits while the operator continues to input telephone order information. If the running subtotal of the order amount exceeds the customer's credit limit, the operator can advise the customer before entering additional line items. Asynchronous mode also is useful for testing inventory levels as the operator enters individual line items with item number and quantity ordered values.

Creating complex, special-purpose Visual Basic 4.0 order entry front-ends is beyond the scope of this book. Most sophisticated order entry applications contain thousands of lines of code, most of which are devoted to implementing a specific set of business services. The sections that follow describe the process of "remoting" the order entry component of an existing, relatively simple OLTP application. The principles outlined in these sections, however, are applicable to a wide variety of OLTP front-ends.

A Candidate Order Entry System for Remote Automation


The Northwind Traders Client/Server Order Entry System (Msa7oltp.mdb) is an Access 95 OLTP application designed to compare the performance of conventional shared-file and client/server front-ends. Msa7oltp first appeared at Microsoft's 1994 Tech*Ed conference as an Access 2.0 application (MSA_OLTP.MDB) to demonstrate the use of Access Basic code-behind-forms and SQL passthrough to Microsoft SQL Server. Subsequently, MSA_OLTP.MDB was converted to the 32-bit Access 95 version, Msa7oltp.mdb, which uses tables attached from Msa7data.mdb (data from Access 2.0's NWIND.MDB sample database converted to Access 95's Jet 3.0 format.)



Msa7oltp.mdb and Msa7data.mdb are located in the \DDG_VB4\32_bit\Chaptr23 folder of the accompanying CD-ROM. You must have Access 95 to run this application. If you locate Msa7data.mdb in a folder other than C:\DDG_VB4\32_bit\Chaptr23, use Access 95's Linked Table Manager to refresh the links to the attached tables. To use SQL passthrough to Microsoft SQL Server 4.21+, export the tables in Msa7data.mdb to a new SQL Server database, nwind, and then create a new SQL Server ODBC data source, NWIND. The Access 95 Upsizing Wizard, available free from http://www.microsoft.com, makes exporting data from Access 95 tables easy. If you don't use the Upsizing Wizard, add clustered (primary key) indexes to the SQL Server tables to improve performance.

Figure 23.5 shows the single data entry form (frmOrderEntry) of Msa7otlp.mdb. The application is designed for heads-down telephone order entry using the keyboard only. (All fields and command buttons have accelerator keys, so use of a mouse is optional). To find an existing customer, you type one or more beginning letters of the customer name in the Bill To text box and press Return. If more than one customer name matches the search entry, you select the customer name from the list box that opens at the lower left of the form and press Return. After the customer is found, a list of the customer's orders appears (last order first) in the lower right list box. The Show Detail command button opens a multicolumn list box that displays the line items that comprise an existing order. (See Figure 23.6.)

Figure 23.5. Selecting in Msa7oltp.mdb a specific customer from a list of existing customers whose names begin with "Q."

Figure 23.6. The Show Detail command button displays the line items that comprise an existing order.

The New Order command button opens a subform whose data source is tblOrder_Details, a local table that holds a temporary set of records representing the line items of the order. The operator enters quantity and item numbers; on exiting the item number field, the remainder of the line item data is filled from the appropriate record of the Products table. (You also can enter one or more of the initial letters of the Product Name field to find a product by name.) The Extended amount is calculated by multiplying Quan(tity) by Unit (Price) and applying the Disc(ount), if any (see Figure 23.7). Extended amounts are not stored in the tblOrder_Details table. The Add New Order button starts a transaction that obtains the next order number, then INSERTs the order in the Orders table, followed by an INSERT of each line item to the Order_Details table. If the COMMIT operation on the transaction is successful, the temporary records in tblOrder_Details are deleted and a multicolumn list box displays the line items comprising the final order. The times to obtain the next order number, set up the transaction, and commit the transaction appear at the bottom of the form (see Figure 23.8). Listing 23.1 shows the Access VBA code that processes the Add New Order transaction.

Figure 23.7. Adding line items to a new order in the sbfOrder_Details subform.

Figure 23.8. The frmOrderEntry form confirming the line items of a newly added order.

Listing 23.1. Access VBA code to add a new order to attached Jet 3.0 tables or by SQL passthrough to SQL Server tables.




Private Sub AddNewOrder()



   'Purpose: Add a new order to the Orders table and _



   'line items to the Order Details table



   



   On Error GoTo AddOrderError



   



   Dim rsLineItems As Recordset



   Dim strMsg As String



   Dim strSQL As String



   Dim lngOrder_ID As Long



   Dim sngStart As Single



   Dim sngGetID As Single



   Dim sngWrap As Single



   Dim sngCommit As Single



   



   On Error GoTo AddOrderError



   



   sngStart = Timer



   



   If fPassthru Then



      'Use SQL passthrough to SQL Server



      On Error GoTo PassthroughError



      



      'Obtain the tentative order ID



      strSQL = "SELECT MAX(Order_ID) Last_Order FROM Orders"



      Set qdfOrderID = dbCurrent.OpenQueryDef("qdfOrderID")



      qdfOrderID.Connect = strConnect



      qdfOrderID.SQL = strSQL



      qdfOrderID.ReturnsRecords = True



      



      Set rsOrders = dbCurrent.OpenRecordset("qdfOrderID")



      lngOrder_ID = rsOrders.Last_Order + 1



      txtOrder_ID.Enabled = True



      txtOrder_ID.Value = lngOrder_ID



      sngGetID = Timer - sngStart



      sngStart = Timer



      



      'INSERT the order information (one _very_ long SQL statement)



      strSQL = "BEGIN TRAN INSERT Orders VALUES(" & _



         txtOrder_ID & ", '"



      strSQL = strSQL & txtCustomer_ID & "', "



      strSQL = strSQL & cboEmployee_ID & ", '"



      strSQL = strSQL & txtShip_Name & "', '"



      strSQL = strSQL & txtShip_Address & "', '"



      strSQL = strSQL & txtShip_City & "', '"



      strSQL = strSQL & txtShip_Region & "', '"



      strSQL = strSQL & txtShip_Postal_Code & "', '"



      strSQL = strSQL & txtShip_Country & "', "



      strSQL = strSQL & cboShip_Via & ", '"



      strSQL = strSQL & txtOrder_Date & "', '"



      strSQL = strSQL & txtRequired_Date & "', "



      strSQL = strSQL & "null, null) "



      



      'INSERT the line item information for each record



      rsLineItems.MoveFirst



      Do Until rsLineItems.EOF



         strSQL = strSQL & "INSERT Order_Details VALUES(" & _



            lngOrder_ID & ", "



         strSQL = strSQL & rsLineItems!Product_ID & ", "



         strSQL = strSQL & rsLineItems!Unit_Price & ", "



         strSQL = strSQL & rsLineItems!Quantity & ", "



         strSQL = strSQL & rsLineItems!Discount & ") "



         rsLineItems.MoveNext



         If rsLineItems.EOF Then



            'Finish the transaction



            strSQL = strSQL & "COMMIT TRAN"



         End If



      Loop



      sngWrap = Timer - sngStart



      sngStart = Timer



      



      'Execute the SQL statement



      Set qdfNewOrder = dbCurrent.OpenQueryDef("qdfNewOrder")



      qdfNewOrder.Connect = strConnect



      qdfNewOrder.SQL = strSQL



      qdfNewOrder.ReturnsRecords = False



      qdfNewOrder.Execute



      



      sngCommit = Timer - sngStart



   Else



      'Use attached Msa7data.mdb tables



      'Obtain a tentative order ID



      lngOrder_ID = DMax("Order_ID", "Orders") + 1



      txtOrder_ID.Enabled = True



      txtOrder_ID.Value = lngOrder_ID



      sngGetID = Timer - sngStart



      sngStart = Timer



      



      'Add order and detail items as a transaction



      On Error GoTo RollbackOrder



      wsCurrent.BeginTrans



         'Add the order to the Orders table



         rsOrders.AddNew



         rsOrders!Order_ID = lngOrder_ID



         rsOrders!Customer_ID = txtCustomer_ID



         rsOrders!Employee_ID = cboEmployee_ID



         rsOrders!Ship_Name = txtShip_Name



         rsOrders!Ship_Address = txtShip_Address



         rsOrders!Ship_City = txtShip_City



         rsOrders!Ship_Region = txtShip_Region



         rsOrders!Ship_Postal_Code = txtShip_Postal_Code



         rsOrders!Ship_Country = txtShip_Country



         rsOrders!Ship_Via = cboShip_Via



         rsOrders!Order_Date = txtOrder_Date



         rsOrders!Required_Date = txtRequired_Date



         'Following fields are not updated for a new order



         'rsOrders!Shipped_Date = txtShipped_Date



         'rsOrders!Freight = txtFreight



         rsOrders.UPDATE



            



         'Add the line item(s) to the Order Details table



         rsLineItems.MoveFirst



         Do Until rsLineItems.EOF



            rsOrderDetails.AddNew



            rsOrderDetails!Order_ID = lngOrder_ID



            rsOrderDetails!Product_ID = rsLineItems!Product_ID



            rsOrderDetails!Quantity = rsLineItems!Quantity



            rsOrderDetails!Unit_Price = rsLineItems!Unit_Price



            rsOrderDetails!Discount = rsLineItems!Discount



            rsOrderDetails.UPDATE



            rsLineItems.MoveNext



         Loop



         sngWrap = Timer - sngStart



         sngStart = Timer



      wsCurrent.CommitTrans



      sngCommit = Timer - sngStart



   End If



   



   On Error GoTo AddOrderError



   



   'Prevent further editing



   Call LockOrderData



   



   'Display the order details in the listbox



   Call cmdShowDetail_Click



   



   'Display the transaction times



   Call HideTimerData



   txtGetID.Value = sngGetID



   txtWrap.Value = sngWrap



   txtCommit.Value = sngCommit



   Call ShowTimerData



   



   DoCmd.Hourglass False



   fNewOrder = False



   Exit Sub



   



PassthroughError:



   DoCmd.Hourglass False



   MsgBox Error$, 48, "Unable to Add Order " & lngOrder_ID



   Exit Sub



   



RollbackOrder:



   DoCmd.Hourglass False



   MsgBox Error$, 48, "Unable to Add Order " & lngOrder_ID



   wsCurrent.Rollback



   cmdAddNewOrder.Enabled = True



   cmdCancelNewOrder.Enabled = True



   Exit Sub



   



AddOrderError:



   DoCmd.Hourglass False



   MsgBox Error$, 48, "Order Entry Error"



   Exit Sub



End Sub


Field entry completion and form housekeeping code in the preceding AddNewOrder subprocedure has been removed to conserve space.

The candidate code for a Remote Automation Object is the SQL Server transaction processing code between the If fPassthru Then and Else statements. In order for readers without Microsoft SQL Server to test the RAO, both the SQL passthrough and Jet 3.0 database processes are included in the RAO. The sections that follow describe the process for creating an OrderServer.Order class with an AddNewOrder method, testing the OrderServer.Order class as a local OLE Automation server, then converting the OrderServer.Order class to a RAO, the RemoteServer.Order class. (Fully qualified object names allow use of two versions of the Order.AddNewOrder method for testing.

Determining an OLTP Design or Conversion Strategy


The "Creating OLE Servers" section of the Professional Features manual for Visual Basic 4.0 and the Building Client/Server Applications with Visual Basic manual that accompanies the Enterprise Edition provide the guidelines for creating local and remote OLE Automation servers. Many Visual Basic 4.0 OLE Automation servers, however, are likely to be derived from existing Visual Basic 3.0/4.0 or Access database applications, as previously observed in Chapter 16.

Following is a list of the primary considerations in determining a conversion strategy for creating Remote Automation servers for Visual Basic database applications:

The preceding design recommendations apply equally to new object classes you create or code in existing applications you convert to RAOs. It's especially important to observe these recommendations during a conversion process, because moving blocks of existing code to an OLE Automation server is a deceptively simple process. Simply wrapping a block of code as a class (without optimizing the code for use as a Remote Automation server) can lead to excessive network traffic and very poor performance.

Following is a list of the suggested steps for creating a Remote Automation OLTP server from existing code:

  1. Write the test application before you create the server code. It's likely that the techniques you use to pass data to and from the RAO differ substantially from those employed by a conventional application.

  2. Determine which Public or form-level Dim variable values are required by the server. You must pass these values to the server or establish the values independently of the client.

  3. Move the block(s) of code to be converted into a new module of your test application without Public variable declarations. (Public variable declarations in class modules define objects, not variables.) Make sure that the code in the new module does not rely on variables declared Public elsewhere in your application.

  4. Test the internal (self-contained) version of your procedures in the new module.

  5. Create a local OLE Automation server with a class module that contains the code from the new module of the test application. Chapter 16 describes the steps for converting a group of existing procedures to a single class.

  6. Exhaustively test the local OLE Automation server with the test application. It is much easier to debug a local server than an RAO.

  7. Compile the test client and remote server, then use Visual Basic 4.0's Setup Wizard to create Setup files for both the server and the client. A full complement of Setup files for the client and the server are the most convenient method to implement remote automation.

  8. Install the test application on the client and the RAO on the server and then test the application.

The preceding sequence of steps is used to create the TstOrder.vbp, OrderSvr.vbp, and OrderRem.vbp projects described in the following sections.

Creating the Test Application for the Server


The TstOrder.vbp application provides the order data used to test the internal AddTestOrder function and the AddNewOrder method of the Orders object exposed by the local and remote server. Figure 23.9 shows the test application adding orders to the NWIND SQL Server data source for the SQL Server 6.5 nwind database that contains tables exported from Msa7data.mdb. You can run TstOrder.vbp independently of OrderSvr.vbp or the Remote Automation server you create from OrderRem.vbp. Click the Get Order Data button to obtain the sample data and then click Internal Function Test button to run the internal function code that is converted to a method of the Order class. The values in the three text boxes are used to compare the speed of operation of the three different implementations of the new order entry code.

Figure 23.9. The frmTestOrd form displaying the result of using the local AddTestOrder function to add an order with 10 line items to the SQL Server data source.



The TstOrder.vbp project is located in the \DDG_VB4\32_bit\Chaptr23 folder of the accompanying CD-ROM. To run TstOrder.vbp with Msa7data.mdb, the database must be in the same folder as the files of TstOrder.vbp. To run TstOrder.vbp with the SQL Server NWIND data source in SQL passthrough mode, you must create the nwind database, export the tables in Msa7data.mdb to the nwind database, then create the NWIND 32-bit ODBC data source.

Listing 23.2 shows the three most important subprocedures of the frmTestOrd form. The Form_Load event handler creates a Recordset of the Snapshot type over the Customers and tblOrder_Details tables. The cmdInternalTest_Click event handler picks a random Customer_ID value, then creates a Recordset of the orders for the customer to provide the field test values for records added to the Orders table. The tblOrder_Details table has 10 line item records to append to the Order_Details table with each new order added. The most important change to the original code of Listing 23.1 is that in the cmdInternalTest_Click event handler for the creation of Variant arrays to hold the field values for the records added to the Orders (order header) and Order_Details (line items) tables.

Listing 23.2. The three primary subprocedures of the frmTstOrder form.




Private Sub Form_Load()



   'Open the Customers and tblOrder_Details tables



   strConn = App.Path & "\Msa7data.mdb"



   Set dbCurrent = DBEngine.Workspaces(0).OpenDatabase(strConn)



   Set rsCustomers = dbCurrent.OpenRecordset("Customers", _



      dbOpenSnapshot)



   rsCustomers.MoveLast 'To get valid RecordCount



   Set rsItems = dbCurrent.OpenRecordset("tblOrder_Details", _



      dbOpenSnapshot)



   rsItems.MoveLast     'To get valid RecordCount



End Sub



Private Sub cmdGetData_Click()



   Do While True



      'Pick a random customer



      Randomize



      rsCustomers.PercentPosition = 100 * Rnd()



      txtCustomer.Text = rsCustomers!Company_Name



      'Find orders for random customer



      strSQL = "SELECT * FROM Orders WHERE Orders.Customer_ID = '" 



         & rsCustomers!Customer_ID & "'"



      Set rsCustOrders = dbCurrent.OpenRecordset(strSQL, _



         dbOpenDynaset)



      If rsCustOrders.RecordCount > 0 Then



         'Get the actual record count



         rsCustOrders.MoveLast



         'Pick a random order



         rsCustOrders.PercentPosition = 100 * Rnd()



         Exit Do



      End If



   Loop



   



   'Create a Variant array of order field values



   ReDim avarOrder(rsCustOrders.Fields.Count - 1)



   For intCtr = 0 To rsCustOrders.Fields.Count - 1



      avarOrder(intCtr) = rsCustOrders(intCtr)



   Next intCtr



   



   'Create a two-dimension Variant array of line items to add



   intRows = rsItems.RecordCount - 1



   'Don't include the Product Name, Quantity per Unit,



   'or Extended columns



   intCols = rsItems.Fields.Count - 4



   ReDim avarItems(intRows, intCols)



   rsItems.MoveFirst



   For intRow = 0 To intRows



      'Iterate through the rows and columns



      For intCol = 0 To intCols



         avarItems(intRow, intCol) = rsItems(intCol)



      Next intCol



      rsItems.MoveNext



      If rsItems.EOF Then



         'Safety valve



         Exit For



      End If



   Next intRow



   rsCustOrders.Close



   Set rsCustOrders = Nothing



   cmdInternalTest.Enabled = True



   cmdLocalSvrTest.Enabled = True



   cmdRemoteSvrTest.Enabled = True



End Sub



Private Sub cmdInternalTest_Click()



   'Use the AddTestOrder function in modTstOrder



   txtOrder_ID.Text = AddTestOrder(strConn, avarOrder, _



      avarItems, fPassthru, sngGetID, sngWrap, sngCommit)



   cmdInternalTest.Enabled = False



   'Display the execution times



   txtGetID.Text = Format$(sngGetID, "0.000")



   txtWrap.Text = Format$(sngWrap, "0.000")



   txtCommit.Text = Format$(sngCommit, "0.000")



End Sub

Listing 23.3 shows the initial version of the code that ultimately is used to implement the AddNewOrder method of the Order class. No error trapping is provided in the AddTestOrder function in order to aid in diagnosing errors during the debugging process. With an internal function, all variables can be passed ByRef (the default), so the parameters of the function do not include a ByRef or ByVal prefix. The Ubound function is used to determine the upper limits of the arrays; using Ubound (instead of literal numbers) makes the code more generic.

Listing 23.3. The initial (internal) test version of the AddNewOrder method of the Orders class.




Public Function AddTestOrder(strConn As String, _



                             avarOrder As Variant, _



                             avarItems As Variant, _



                             fUseSQL As Boolean, _



                             sngGetID As Single, _



                             sngWrap As Single, _



                             sngCommit As Single) As Long



   Dim dbRemote As Database



   Dim wsRemote As Workspace



   Dim rsOrders As Recordset



   Dim rsOrderDetails As Recordset



   Dim lngOrder_ID As Long



   Dim strSQL As String



   Dim rsOrder_ID As Recordset



   Dim intCtr As Integer



   Dim intRow As Integer



   Dim intCol As Integer



   Dim intPos As Integer



   



   Set wsRemote = DBEngine.Workspaces(0)



   sngGetID = Timer



   If fUseSQL Then



      Set dbRemote = wsRemote.OpenDatabase("", False, _



         False, strConn)



      'Find the last order number



      strSQL = "SELECT MAX(Order_ID) Last_Order FROM Orders"



      Set rsOrder_ID = dbRemote.OpenRecordset(strSQL, _



         dbOpenSnapshot, dbSQLPassThrough)



      'Increment the last order number



      lngOrder_ID = rsOrder_ID(0) + 1



      If lngOrder_ID > 1 Then



         sngGetID = Timer - sngGetID



         sngWrap = Timer



         'INSERT the order information (one very long 



         'SQL statement)



         strSQL = "BEGIN TRAN INSERT Orders VALUES(" & _



            lngOrder_ID & ", "



         'Don't insert Shipped_Date or Freight



         For intCtr = 1 To UBound(avarOrder) - 2



            If VarType(avarOrder(intCtr)) = vbDate _



                  Or VarType(avarOrder(intCtr)) = vbString Then



               'Two consecutive single quotes for "'"



               If VarType(avarOrder(intCtr)) = vbString _



                     And InStr(avarOrder(intCtr), "'") Then



                  intPos = InStr(avarOrder(intCtr), "'")



                  'The following substitutes "''" for "'"



                  avarOrder(intCtr) = Left(avarOrder(intCtr), _



                     intPos) & Mid(avarOrder(intCtr), intPos)



               End If



               'Use single quotes for dates and strings



               strSQL = strSQL & "'" & avarOrder(intCtr) & "', "



            ElseIf VarType(avarOrder(intCtr)) = vbNull Then



               strSQL = strSQL & "null, "



            Else



               strSQL = strSQL & avarOrder(intCtr) & ", "



            End If



         Next intCtr



         strSQL = strSQL & "null, null) "



      



         'INSERT the line item information for each record



         For intRow = 0 To UBound(avarItems, 1)



            strSQL = strSQL & "INSERT Order_Details VALUES(" & _



               lngOrder_ID & ", "



            For intCol = 1 To UBound(avarItems, 2)



               If intCol = UBound(avarItems, 2) Then



                  strSQL = strSQL & avarItems(intRow, intCol) & ") "



               Else



                  strSQL = strSQL & avarItems(intRow, intCol) & ", "



               End If



            Next intCol



         Next intRow



         strSQL = strSQL & "COMMIT TRAN"



      



         sngWrap = Timer - sngWrap



         sngCommit = Timer



         'Execute the SQL statement



         dbRemote.Execute strSQL, dbSQLPassThrough



      Else



         'Error condition



      End If



   Else



      Set dbRemote = wsRemote.OpenDatabase(strConn)



      Set rsOrders = dbRemote.OpenRecordset("Orders", _



         dbOpenDynaset)



      Set rsOrderDetails = dbRemote.OpenRecordset("Order Details", _



         dbOpenDynaset)



      



      'Get the last order number and increment



      rsOrders.MoveLast



      lngOrder_ID = rsOrders(0) + 1



      sngGetID = Timer - sngGetID



      sngWrap = Timer



      



      'Add order and detail items as a transaction



      wsRemote.BeginTrans



         'Add the order record to the Orders table



         rsOrders.AddNew



         'Add the new Order_ID



         rsOrders(0) = lngOrder_ID



         'Add the field values



         For intCtr = 1 To UBound(avarOrder)



            rsOrders(intCtr) = avarOrder(intCtr)



         Next intCtr



         'Update the values



         rsOrders.Update



         'Add the line item(s) to the Order Details table



         For intRow = 0 To UBound(avarItems, 1)



            'New row



            rsOrderDetails.AddNew



            'Add the Order_ID as column 0



            rsOrderDetails(0) = lngOrder_ID



            'Add the valid field values



            For intCol = 1 To UBound(avarItems, 2)



               rsOrderDetails(intCol) = avarItems(intRow, intCol)



            Next intCol



            rsOrderDetails.Update



          Next intRow



          sngWrap = Timer - sngWrap



          sngCommit = Timer



      wsRemote.CommitTrans



      Set rsOrders = Nothing



      Set rsOrderDetails = Nothing



   End If



   Set dbRemote = Nothing



   Set wsRemote = Nothing



   sngCommit = Timer - sngCommit



   AddTestOrder = lngOrder_ID



End Function

Following is a typical Transact-SQL statement created by the preceding code when adding an order to the SQL Server nwind database. An ellipsis (. . .) replaces the INSERT . . . VALUES statements for line items 3 through 9. Use of the VALUES function requires a value for each field; null is used if no field value is supplied. Using the VALUES function is faster than specifying multiple field names and their values.




BEGIN TRAN 



   INSERT Orders 



      VALUES(11057, 'OTTIK', 2, 'Ottilies Käseladen', 



      'Mehrheimerstr. 369', 'Köln', null, '50739', 



      'Germany', 1, '3/2/92', '3/16/92', null, null) 



   INSERT Order_Details 



      VALUES(11057, 1, 18, 10, 0.05) 



   INSERT Order_Details 



      VALUES(11057, 2, 19, 5, 0) 



   ...



   INSERT Order_Details 



      VALUES(11057, 10, 31, 10, 0.15) 



COMMIT TRAN

Converting the AddTestOrder Function to the AddNewOrder Method of the Orders Class


Relatively few but important changes to the code for the internal AddTestOrder function are required to add error handling and specify ByVal and ByRef handling of parameters for the AddNewOrder method of the Order class. Listing 23.4 shows only the changes to the code for the AddTestOrder function of Listing 23.3 required to minimize marshalling operations, accommodate Variant arrays, and provide primitive error handling. Code duplicated in Listing 23.3 is replaced by ellipses (. . .) to conserve space. Figure 23.10 shows Object Browser displaying the syntax of the AddNewOrder method. ByVal and ByRef prefixes do not appear in Object Browser's syntax listing.



If you do not prefix the avarOrder and avarItems parameters with ByRef, you receive an Invalid procedure call error when executing the subprocedure that creates an instance of the Order object. The ByVal prefix is not optional for multidimensional Variant arrays.

Listing 23.4. Changes to the initial (internal) test code of Listing 23.3 to create the AddNewOrder method of the Orders class.




Public Function AddNewOrder(ByVal strConn As String, _



                            ByVal avarOrder As Variant, _



                            ByVal avarItems As Variant, _



                            ByVal fUseSQL As Boolean, _



                            ByRef sngGetID As Single, _



                            ByRef sngWrap As Single, _



                            ByRef sngCommit As Single, _



                            ByRef lngErrNum As Long, _



                            ByRef strError As String) As Long



   



   ... 'Dim statements



   



   'Provide error handler



   On Error GoTo AddNewOrderErr



   



   ... 'Order processing code



   



   Exit Function



AddNewOrderErr:



   'Primitive error handling



   lngErrNum = Err.Number



   strError = Err.Description



   Set dbRemote = Nothing



   Set wsRemote = Nothing



   Exit Function



End Function


The code for the OrderSvr.vbp project is located in the same folder of the CD-ROM as TstOrder.vbp. Compile and run OrderSvr.vbp in a separate instance of 32-bit Visual Basic 4.0. Click the Get Order Data button of TstOrder.vbp and then click Local Server Test to check performance of the local server.

Figure 23.10. Object Browser displaying the syntax for the AddNewOrder method of the Order class.



You must add a reference to the Microsoft DAO 3.0 Object Library or the Microsoft Remote Data Object 1.0 to OLE Automation servers that connect to databases.

Listing 23.5 shows the code of the cmdLocalSvrTest_Click event handler that creates a local instance of the Orders object when you apply the AddNewOrder function. Calling a method, or getting or setting a property value, of an object specified by the Dim objName As New ObjectClass is required to instantiate the object.

Listing 23.5. The cmdLocalSvrTest_Click subprocedure for testing the local server version of the AddNewOrder method of the Order class.




Private Sub cmdLocalSvrTest_Click()



   'Test the local OLE Automation server (OrderSvr.vbp)



   Dim objNewOrder As New OrderServer.Order



   'Instantiate the Order object with the method call



   txtOrder_ID.Text = objNewOrder.AddNewOrder(strConn, _



      avarOrder, avarItems, fPassthru, sngGetID, _



      sngWrap, sngCommit, lngErrNum, strError)



   cmdLocalSvrTest.Enabled = False



   txtGetID.Text = Format$(sngGetID, "0.000")



   txtWrap.Text = Format$(sngWrap, "0.000")



   txtCommit.Text = Format$(sngCommit, "0.000")



   Set objNewOrder = Nothing



End Sub

Deploying Remote Automation Servers and Client Applications


Deploying a Remote Automation server and setting up client applications that use the remote server is a relatively simple task if you use Visual Basic 4.0 Setup Wizard to create a Setup program that automatically supplies the files necessary to implement Remote Automation. Use of the Setup Wizard for conventional Visual Basic 4.0 applications is described in Chapter 27, "Making Distribution Diskettes with the Visual Basic Setup Kit." The required auxiliary files are described the "Remote Automation Components" section, near the beginning of this chapter. You first install the Remote Automation server on the application server and then create the setup files installation of the client application on users' workstations. The sections that follow describe the process for deploying the remote version (OrderRem.vbp) of the local server (OrderSvr.vbp) described in the preceding sections.



OrderRem.vbp is created by opening a new 32-bit Visual Basic 4.0 project, removing the Form1 file, and adding the clsOrder.cls and modMain.bas to the project. The value of the Project Name property of OrderRem.vbp is RemoteServer, in order to distinguish between the remote version of the Order class (RemoteServer.Order) and the local version (OrderServer.Order).


Creating the Remote Server Test Subprocedure


The TstOrder.vbp client lets you compare the performance of three different architectures with Jet 3.0 and SQL Server databases. Minor modifications to the cmdLocalSvrTest_Click event handler are required to create the cmdRemoteSvrTest_Click subprocedure (see Listing 23.6). The most important change is the connection string (strRemConn) for the Jet 3.0 database. You must specify explicitly the location of Msa7data.mdb on the server; the App.Path statement does not work in this case, because App.Path returns the location of the client application. The cmdRemoteSvrTest_Click event handler also includes a message box to report errors returned from the server.

Listing 23.6. Revisions to the cmdRemoteSvrTest_Click event handler to specify .mdb file location and to provide error reporting.




Private Sub cmdRemoteSvrTest_Click()



   'Test the remote OLE Automation server (OrderRem.vbp)



   Dim strRemConn As String



   Dim lngRetVal As Long



   Dim objNewOrder As New RemoteServer.Order



   If fPassthru Then



      strRemConn = strConn



   Else



      'App.Path does not work for remote server



      'Location of file must be hard coded



      strRemConn = "C:\OrderRem\Msa7data.mdb"



   End If



   'Instantiate the Order object with the method call



   lngRetVal = objNewOrder.AddNewOrder(strRemConn, _



      avarOrder, avarItems, fPassthru, sngGetID, _



      sngWrap, sngCommit, lngErrNum, strError)



   txtOrder_ID.Text = lngRetVal



   cmdRemoteSvrTest.Enabled = False



   txtGetID.Text = Format$(sngGetID, "0.000")



   txtWrap.Text = Format$(sngWrap, "0.000")



   txtCommit.Text = Format$(sngCommit, "0.000")



   Set objNewOrder = Nothing



   If lngRetVal = 0 Then



      'An error occurred in the remote server



      MsgBox "Error " & lngErrNum & ": " & strError



   End If



End Sub

Creating and Installing the Remote Server Setup Files


The process for creating remote server setup files with the Setup Wizard and installing files on the remote server is as follows:

  1. Choose File | Make EXE File to open the Make EXE File dialog.

  2. Click the Options button to open the EXE Options dialog.

  3. Enter the title of the application (usually the same name as assigned to the project) and mark the Remote Server Support Files check box. (See Figure 23.11.)

    Figure 23.11. Setting the application name and specifying the addition of remote server support files in the EXE Options dialog.

  4. Click OK to close the EXE Options dialog; then click OK to close the dialog and compile your file as ProjectName.exe (in this case, OrderRem.exe). The compilation process also creates a ProjectName.vbr file (OrderRem.vbr for this example). You can set the security (authentication) level for each class by modifying this file before you deploy the Remote Automation server on a computer running Windows NT.

  5. Run the server's .exe file locally to ensure its proper registration. This is an important step. You can verify that the server is properly registered by running Racmgr32.exe from your \VB4\Clisvr folder and verifying that the server appears in the Remote Automation Connection Manager's OLE Classes list as a local server. (Contrary to the reference on Page 133 of Building Client/Server Applications with Visual Basic, the Remote Automation Connection Manager's filename is Racmgr32.exe, not "RACREG32.EXE"; Racreg32.dll is a DLL).

  6. From the Start menu, launch the 32-bit Visual Basic 4.0 Application Setup Wizard. If you have previously compiled the .exe file, make sure no other application is using the local copy of the .exe file.

  7. In the Step 1 dialog, enter the location of the project file and click the Next button. (Recompiling the .exe file is optional.)

  8. Click Next in the Step 2 dialog and enter the location of a folder in which to create the setup files in the Step 3 dialog. (A shared folder of the application server is a convenient place for the setup files.) Click Next.

  9. If your server does not require other custom OLE Automation servers, click Next to bypass the Step 4 dialog. Otherwise, click the Remote Server Details button to display the Remote OLE Server Details dialog to enter the network location and network protocol. (These settings are discussed in the following section.)

  10. The Step 5 dialog lists the file dependencies of your server, Olepro32.dll and, for 32-bit database applications, Dao3032.dll. If current versions of these files are installed on the server, you don't need to include the files in the setup folder. Click Next.

  11. The Step 6 dialog lets you choose between installing the OLE 2.1 components in a specified application folder or in a shared \Olesvr folder. Msa7data.mdb must be located in the same folder as the OLE server, so you select Install in application directory as the Deployment Model. Mark the check box labeled Yes, install remote OLE [A]utomation server components. (See Figure 23.12).

    Figure 23.12. The Step 6 dialog of the Setup Wizard lets you choose the deployment method and add the Remote Automation auxiliary files.

  12. In the Step 7 dialog, add any additional files required by your server to the list. In this case, Msa7data.mdb is necessary, if you don't want to be restricted to use of the SQL passthrough option. (See Figure 23.13.)

    Figure 23.13. Adding additional required files in the Step 7 dialog of the Setup Wizard.

  13. Click Finish to produce the setup files, which generated 31 compressed data files occupying about 3.6MB of disk space. When the Finished dialog appears, click Exit to close the Setup Wizard.

  14. Run the Setup.exe program on the server to start the setup process. The RemoteServer Setup window appears. Click OK to continue.

  15. Setup proposes to install the OLE Server in the C:\Exename folder, in this case, C:\OrderRem. Accept the default or choose a different folder. Click the Install button to complete the setup process.

  16. Setup installs a Remote Automation Management group in the Start menu of Windows 95 or Windows NT 4.0 to launch Automation Manager (Autmgr32.exe) and Remote Connection Manager (Racmgr32.exe). You must start Automation Manager in order for clients to connect to remote objects. You automatically start Automation Manager as a service on a production server.

  17. If you have SQL Server installed, either on the applications server or on a database server, create a NWIND system data source for the nwind database. You must create a system data source so that the data source is not dependent on the current user logged on the server.

The content of the OrderRem.vbr file created during compilation of OrderRem.vbp, which provides the Registry information required by the remote server, is as follows:




VB4SERVERINFO



VERSION=1.0.0



APPDESCRIPTION=Remote Automation Server for Northwind



HKEY_CLASSES_ROOT\Typelib\{3CD04C66-9157-11CF-A658-



00AA002A764D}\1.0 = Remote Automation Server for Northwind



HKEY_CLASSES_ROOT\Typelib\{3CD04C66-9157-11CF-A658-



00AA002A764D}\1.0\0\win32 = OrderRem.exe



HKEY_CLASSES_ROOT\Typelib\{3CD04C66-9157-11CF-A658-00AA002A764D}\1.0\FLAGS = 0



HKEY_CLASSES_ROOT\RemoteServer.Order\CLSID = {3CD04C65-9157-11CF-A658-



00AA002A764D}



HKEY_CLASSES_ROOT\CLSID\{3CD04C65-9157-11CF-A658-



00AA002A764D}\ProgID = RemoteServer.Order



HKEY_CLASSES_ROOT\CLSID\{3CD04C65-9157-11CF-A658-



00AA002A764D}\Typelib = {3CD04C66-9157-11CF-A658-00AA002A764D}



HKEY_CLASSES_ROOT\CLSID\{3CD04C65-9157-11CF-A658-



00AA002A764D}\InprocHandler32 = OLE32.DLL



HKEY_CLASSES_ROOT\CLSID\{3CD04C65-9157-11CF-A658-



00AA002A764D}\LocalServer32 = OrderRem.exe



HKEY_CLASSES_ROOT\INTERFACE\{3CD04C64-9157-11CF-A658-00AA002A764D} = Order



HKEY_CLASSES_ROOT\INTERFACE\{3CD04C64-9157-11CF-A658-



00AA002A764D}\ProxyStubClsid = {00020420-0000-0000-C000-000000000046}



HKEY_CLASSES_ROOT\INTERFACE\{3CD04C64-9157-11CF-A658-



00AA002A764D}\ProxyStubClsid32 = {00020420-0000-0000-C000-000000000046}



HKEY_CLASSES_ROOT\INTERFACE\{3CD04C64-9157-11CF-A658-



00AA002A764D}\Typelib = {3CD04C66-9157-11CF-A658-00AA002A764D}


You must edit the ProjectName.vbr file for the Remote Automation server to add security features before you create the client setup files.


Setting Up Remote Automation Clients


The process for setting up Remote Automation clients is quite similar to that for Remote Automation servers. In addition to the ProjectName.vbr file, the Setup Wizard also creates a ProjectName.tlb type library. Follow these steps to create the client setup files, which you might want to locate on a file server for distribution to users:

  1. You must include a reference to a copy of the remote server's .exe file on your local fixed disk in order to compile your client application that refers to the remote server. You cannot include a reference to both local and remote copies of the same server, so you alter the location of the server in step 3.

  2. Follow steps 1 through 4 and 6 through 8 of the preceding section to compile your client (or test) application and proceed through the Step 1, Step 2, and Step 3 dialogs. (The Remote Server Support Files check box is disabled when you process a client application.)

  3. In the Setup Wizard's Step 4 dialog, delete the reference to the local copy of the Remote Server's .exe file and specify the .vbr file for the remote server, if it appears in the list (see Figure 23.14). If the .vbr file does not appear in the list, click the Add OLE Servers button to open the Add OLE Servers dialog and specify addition of the appropriate .vbr file.

    Figure 23.14. Selecting the OLE Automation servers used by the Remote Automation client.

  4. When you select the .vbr file, the Remote OLE Server Details dialog automatically opens. Enter the network name of the application server on which the Remote Automation server is installed (OAKLEAF0 in this case). Select the network transport protocol to connect to the server from the Network Protocol drop-down list (TCP/IP is used for this example). If you want to establish security for the object, choose the level of security desired from the Authentication drop-down list. (See Figure 23.15.)

    Figure 23.15. Specifying the server name, network transport protocol, and security level for a Remote Automation server.

  5. Continue to the Step 7 dialog and add any additional files required in the folder in which the client application is to be installed. For this example, Msa7data.mdb is required for the test client.

  6. Click Finish to complete the Setup Wizard process and then click Exit to close Setup Wizard.

During installation, the Setup Wizard adds the keys for the remote server to the Registry. Figure 23.16 shows the Registry entries for the RemoteServer.Order class. The entries you make in the Remote OLE Server Details dialog in the preceding step 4 sets the values of the AllowRemoteActivation, NetwordAddress, and ProtocolSequence keys.



You can use the Client pane of the Remote Automation Connection Manager (Racmgr32.exe) to change the values of the Registry keys for the remote server. Using Racmgr32.exe for this purpose is safer than manually modifying the values of the keys.

Figure 23.16. Registry entries for the RemoteServer.Order class.



After you run the Setup application for your Remote Automation client, you can choose to run the .exe file or run the client source code in Visual Basic 4.0. The client Setup process is required primarily to install the Registry keys for the Remote Automation object class.


Testing the Remote Automation Server


Run TstOrder.exe, click Get Order Data, then click Remote Server Test to instantiate the RemoteServer.Order object and add an order or two to the database. The performance of the Creatable MultiUse version of the remote server, as measured by the values shown in the transaction processing time text boxes (see Figure 23.17), is slightly slower than the local server version. The times displayed, however, do not include the overhead of creating an instance of the server.

Figure 23.17. Transaction processing times for adding an order to the SQL Server nwind database with the RemoteServer.Order object.



Many errors returned during testing of your remote server are difficult to diagnose because Remote OLE Automation error codes and messages often are ambiguous or uninformative as to the actual source of the problem. For an explanation of the most likely source of the most common Remote OLE Automation errors, download Microsoft Knowledge Base article Q141824, "Common Remote OLE Automation Errors," from CompuServe (GO MSKB) or from http://www.microsoft.com. This article contains information, not included in the documentation that accompanies Visual Basic 4.0, that's helpful during the debugging process.

If you are running SQL Server and the Remote Automation server on the same computer, the time required by the remote server to process the first order by SQL passthrough is substantially faster than either the internal function call or the local server. Establishing a remote connection to SQL server is responsible for this time difference, a one-time performance hit. (The remote server has a permanent local connection to SQL Server.) Subsequent order additions with the remote server are somewhat slower than with the internal function call or local server. The reduction in speed is due to the time required to instantiate a RemoteServer.Order object.



Using a pool manager to provide multiple instances of Creatable SingleUse remote servers improves the performance, especially for multiple simultaneous users of an object class. The pool manager asynchronously creates a pool of objects of a specific class for use by client applications. Creating an object prior to the client's request for instantiation of the object saves the time necessary to instantiate the object in response to each client request. A RAO consumes about 500KB of server memory, so you need to be conservative in your allocation of server resources to pooled RAOs. The \Vb4\Samples\Remauto\Poolmngr folder contains an example of a Visual Basic 4.0 pool manager application.


Using the Component Manager


The Component Manager is designed to encourage sharing of OLE Automation servers with other developers. You publish the characteristics of your server in a catalog that other developers can search to find an object class that meets their needs. To add your Remote Automation sever to the Component Manager catalog included with the Enterprise Edition of Visual Basic 4.0, follow these steps:

  1. Launch Component Manager (Cmpmgr32.exe) from the \Vb4\clisvr folder. The Component Manager catalog includes entries for each of the OLE server sample applications that accompany the Enterprise Edition.

  2. Choose Results | Add OLE Component to open the Add OLE Component dialog.

  3. With the Exe/TypLib option selected, click the browse button to open the Select Exe or TypLib file for OLE Server dialog.

  4. Locate and select your Remote Automation server, in this case \\Oakleaf0\croot\OrderRem\OrderRem.exe (see Figure 23.18). Click the Open button to add the server to the Available Object Classes list of the Add OLE Component dialog (see Figure 23.19) and close the Select Exe or TypLib file for OLE Server dialog.

    Figure 23.18. Locating the Remote Automation server's .exe file for addition to Component Manager's catalog.

    Figure 23.19. Adding the Remote Automation server to the Catalog.

  5. Click the Add button and then the Close button to add the Remote automation object to the catalog. (See Figure 23.20.)

Figure 23.20. An entry for the RemoteOrder class added to Component Manager's Sample Components catalog.

Summary


The ability to create and deploy Remote Automation servers is one of the primary selling features of the Enterprise Edition of Visual Basic 4.0. This chapter described the architecture of the current version of Remote Automation that uses Remote Procedure Calls to instantiate OLE Automation objects across networks. The sample application of this chapter, derived from an existing Access 95 database application, demonstrated techniques for coding three-tier online transaction-processing applications. Step-by-step procedures for deploying remote servers and client applications that rely on remote servers also were covered. The chapter concluded with a brief description of the use of the Enterprise Edition's Component Manager to catalog OLE Automation servers.

The next chapter, "Managing Large Projects with Visual SourceSafe 4.0," deals with the process of creating Visual Basic 4.0 database applications in a multi-developer environment. Although source code control is critical to the successful completion and subsequent maintenance of any Visual Basic application, source code control is particularly important during the development of sophisticated Remote Automation servers that provide a uniform set of business services to multiple client applications.

Previous Page Page Top TOC Next Page