Compartilhar via


Microsoft Transaction Server for Visual FoxPro Developers 

 

Randy Brown
Microsoft Corporation

October 1998

Summary: Discusses using Microsoft® Visual FoxPro® version 6.0 with MTS to develop three-tier applications. (36 printed pages).

Contents

Introduction What Is Microsoft Transaction Server? Why Is MTS Important for Visual FoxPro Developers? Creating Your First MTS Server Setting Up Security The Basic Features of MTS Just-In-Time Activation Transactions Programming Models Deployment Remote Deployment and Administration Security Shared Property Manager MTS Support for Internet Information Server Automating MTS Administration Tips and Tricks

Click to copy the sample files associated with this technical article.

Introduction

No doubt you've heard all about Microsoft Transaction Server (MTS) and how it will make your life easier to develop three-tier applications. This article offers a good primer on using Visual FoxPro 6.0 with MTS. We cover the basics of using MTS and then extend it to using with Visual FoxPro Component Object Model (COM) Components. This document is intended to be used with the Microsoft PowerPoint® slide show included with the Visual FoxPro sample files.

MTS is a great environment for working with three-tier development. However, one should realize that it is simply not just a matter of dropping your Visual FoxPro servers into an MTS package and expecting miracles. While it is true that much of the work is already done for you, nothing comes for free. Performance and scalability are critical factors that require well-thought-out designs. Good MTS applications are designed with MTS in mind from the start!

This article assumes that you have MTS already installed. It is available in the Microsoft Windows NT® version 4.0 Option Pack, available from the Microsoft Web site at https://www.microsoft.com/windows/downloads/default.asp.

In addition, you should familiarize yourself with the basics of MTS. Information is available in the Help files provided with MTS when you install the Windows NT 4.0 Option Pack.

What Is Microsoft Transaction Server?

MTS is a component-based transaction processing system for building, deploying, and administering robust Internet and intranet server applications. In addition, MTS allows you to deploy and administer your MTS server applications with a rich graphical tool (MTS Explorer). MTS provides the following features:

  • The MTS run-time environment.
  • The MTS Explorer, a graphical user interface for deploying and managing application components.
  • Application programming interfaces (APIs) and resource dispensers for making applications scalable and robust. Resource dispensers are services that manage nondurable shared state on behalf of the application components within a process.

The MTS programming model provides a framework for developing components that encapsulate business logic. The MTS run-time environment is a middle-tier platform for running these components. You can use the MTS Explorer to register and manage components executing in the MTS run-time environment.

The three-tier programming model provides an opportunity for developers and administrators to move beyond the constraints of two-tier client/server applications. You have more flexibility for deploying and managing three-tier applications because:

  • The three-tier model emphasizes a logical architecture for applications, rather than a physical one. Any service may invoke any other service and may reside anywhere.
  • These applications are distributed, which means you can run the right components in the right places, benefiting users and optimizing use of network and computer resources.

Why Is MTS Important for Visual FoxPro Developers?

Microsoft is investing a great amount of resources in three-tier development because of a multitude of benefits derived from this architecture. As shown in Figure, Tier 2, the so-called "middle tier," represents the layer where much of the Application Services/Business Logic is stored. Visual FoxPro COM components are ideally suited for this architecture and will play a key role in this tier for many years to come. This middle tier is also where MTS lives.

Figure 1. Web-enabled three-tier architecture

Future applications will consist of Web based front ends using a combination of HTML/XML. While Visual FoxPro data can be used as your database of choice for Tier 3, your applications should be written to communicate to a generic back end. This should be a test of your application's extensibility. "How easy is it to swap back ends—let's say Visual FoxPro database to Microsoft SQL Server™?" There are several options, including Open Database Connectivity (ODBC) and ActiveX® Data Objects (ADO), which provide generic interfaces to data. Remember, your application should be written knowing that any or all of the three tiers can be swapped out independent of each other.

So why is MTS great for Visual FoxPro developers? It should be clear now that the ability to swap out tier components at will makes for a great reusability story. Microsoft has a concept called total cost of ownership (TCO), which means the collective cost of providing and maintaining corporate Information Services. The three-tier model goes a long way toward reducing TCO.

Updating the Presentation layer is very easy because it merely involves one having to refresh his/her browser. Windows front ends consisting of Visual FoxPro/Visual Basic® forms offer more flexibility in user interface, but updating 150 sites can be time-consuming. In addition, one should expect improved UI options available in HTML.

The back-end data is usually the tier that changes the least. Having data managed centrally also reduces costs. Remember that data can be distributed and still managed from one location. It doesn't have to be stored centrally to be managed centrally.

Finally, we get to Visual FoxPro's role in the middle tier. Middle-tier components tend to change most often because they represent business rules, which change as the needs of the business changes. Traditional client/server and monolithic applications would often combine the first two layers into one. This was very inefficient because of the distribution costs in updating sites. Today, with browsers, much of this distribution problem goes away. However, business rules are often complex and can contain sensitive/secure information, so it's not always wise to send these rules back with the HTML to a Web browser. In addition, it can impede performance.

So, we end up with a dilemma. We want to limit the amount of information sent back to the client, but we also want to minimize the number of back and forth trips between client and server, because bandwidth is also a big consideration (more so with the Internet versus an intranet). The best solution is one involving a so-called "Smart Client." Traditionally, the Web browser is thought of as an unintelligent client whose job is to merely display an entire static Web page. Each time something on the page changes, we need to refresh the entire Web page. With dynamic HTML (DHTML), you no longer need to do this. Only parts of the Web page affected need updating. In addition, some of the business rules can (and should) reside on the client, thus reducing round trips to the server. For example, you may want to have your client have simple data validation rules, such as one to ensure a value is not negative. It would be more efficient to perform these sorts of checks on the client. Most of the rules, especially sensitive ones, will exist on the server away from client eyes. It is also important to realize, however, that client-side business rules are subject to change almost as frequently as those on the server. The ATSWeb application (available at https://msdn.microsoft.com/vfoxpro/ats_alpha/default.htm) offers a great example of business rules being applied to both client and server.

MTS provides an environment for hosting your Visual FoxPro middle-tier objects because it handles many of the common tasks, including resource and thread management, security, deployment, application robustness, and transactions. This leaves you, the developer, with only the responsibility of providing business logic specific to your application.

Creating Your First MTS Server

Let's jump right in and create an MTS server, because it's very simple if you already know how to create a Visual FoxPro COM component.

Creating a Visual FoxPro COM Component

  1. Create a new project file called test1.pjx

  2. Create a new program file (PRG) called test1.prg

  3. Add the following code to this program:

    DEFINE CLASS server1 AS custom OLEPUBLIC
    PROCEDURE hello
    RETURN "Hello World"
    ENDPROC
    ENDDEFINE
    

  4. Build the server as a DLL (for example, test1.dll). All MTS components must be created as in-process DLL servers. You now have a server that can be tested directly in Visual FoxPro:

    x=create("test1.server1")
    ? x.hello()
    

Adding the Visual FoxPro COM Component to an MTS Package

A package is a collection of components that run in the same process. Packages define the boundaries for a server process running on a server computer. For example, if you group a Sales component and a Purchasing component in two different packages, these two components will run in separate processes with process isolation. Therefore, if one of the server processes terminates unexpectedly (for instance, because of an application fatal error), the other package can continue to execute in its separate process.

This section describes the task of installing the Visual FoxPro server into the MTS environment.

  1. Launch MTS Explorer.
  2. In the left pane, navigate to the Computers item and select My Computer. You are now looking at the MTS environment.
  3. Click the Packages Installed node to view all default packages installed by MTS. You can think of a Package as a set of components that perform related application functions. For example, an Inventory package might consist of two DLLs, each performing a task related to checking product inventory for a customer order.
  4. Let's create a new package now. Select the Action -> New -> Package menu item.
  5. Click the Create an empty package button. Type in a name for your new package (for example, Foxtest1).
  6. Click the Next button, and then click the Finish button. You should now see your new package added under the Packages Installed node.
  7. Click your new package node (for example, Foxtest1). You should now see two items. The Components folder is where you add new components such as the Visual FoxPro component you just created. The Roles folder is where you set up groups of users (roles) who all share similar access privileges (security). You do not need to add anything to the Roles folder in order to use your Visual FoxPro component with MTS.
  8. Click the Components folder and select the Action -> New -> Component menu item.
  9. Click the Install new component(s) button. This will bring up the Install Components dialog box. Click the Add files button and go to the location where you created your Visual FoxPro server (for example, test1.dll). Select both the .dll and .tlb files. The .tlb file is the type library file containing properties and methods of your server. After selecting these two files, you should see your OLEPUBLIC component listed in the lower panel. Click Finish and you should see your server added to this folder.
  10. At this point, your package is complete and ready to go. Later, we will talk about setting Transaction support. This can be done from the Properties dialog box of your server.

Accessing Your Component

You can now test your new MTS packaged component using a command similar to the one used to test Visual FoxPro after the DLL server was first created.

x=create("test1.server1")
? x.hello()

That's all you need to do! If you go back into the MTS Explorer, you should see the component represented with a spinning icon. Click the Status View to see details about the state of the object.

Figure 2. New component viewed in MTS Explorer

If you release the object (RELEASE x), MTS releases its reference.

Going Forward

We've just discussed the basics of installing your Visual FoxPro server in MTS. Essentially, all we did was wrap the Visual FoxPro component inside an MTS process that manages security, transaction state, fault tolerance, and other common server responsibilities. All Visual FoxPro servers used with MTS are registered this way. The remainder of the article discusses how to take advantage of MTS-specific features such as security and transactions. You can write code in your components that talk directly to the MTS run-time environment. In addition, the above process can be entirely automated, because MTS exposes an administrative Automation interface.

Setting Up Security

So why are we starting out so early with security? Well, sooner or later, you're going to fiddle with some sort of security switch and suddenly that MTS application of yours will no longer work. It's important that you follow these instructions and refer to them later when you decide to add security to your applications.

Note   MTS 2.0 security setup is described in the Readme document. If you have MTS installed on Microsoft Windows® 95, you can skip this section.

Setting System Package Identity

Before you do anything in MTS, it is a good idea to configure the system package for administrating security. When installing MTS, set the system package identity before creating any new packages as follows:

  1. Create a new local Windows NT group named "MTS Administrators" and a new local user named "MTS Administrator."
  2. Add the "MTS Administrator" user to the "MTS Administrators" and "Administrators" groups.
  3. Set the identity of the system package to "MTS Administrator." If this does not work, try setting this to the Administrator user.

Note   You cannot set a package's identity to a group.

  1. Shut down the system package so that it will be restarted with the new identity. You can do this by right-clicking the My Computer icon in MTS Explorer and selecting Shut Down Server Processes.

Adding Security for MTS Packages

You first need to determine whether you want all or just a few components in your Package to have security. Right-click the Package and select Properties. Next, click the Security tab. Then check the Enable authorization checking check box. To enable or disable security at a component level, right-click a component and display the Properties dialog box.

If this is all you do, an "Access is denied" error message is generated when you try to access your component. You MUST associate a valid role with any component marked for security!

Right-click the package's Roles folder and select New Role. Type in a functional role such as Managers, Accountants, and so on.

The new role is added as a subfolder. Right-click this folder to Add New User (you will get a dialog box to Add Users and Groups to Role). Select the user(s) that you want to add to your role. To finish, select the Role Membership folder under each component that is marked for security and add the new role created in step 3 by right-clicking the folder and selecting New Role.

Note   You may still experience the "Access is denied" error message when running your components. There are a couple of possible solutions:

  • Sometimes adding a Group to a role does not work (step 3). You might try adding individual users instead.
  • The user rights for that user are not properly set. Make sure the user account for the identities of the system package and other MTS packages have the Windows NT "Log on as a service" user right. You can verify this by using the Windows NT User Manager:
  1. From the Policies menu, select User Rights.
  2. Click Show Advanced User Rights.

Tips for Visual FoxPro Users

Much of the security administration can easily be handled by Automation using the MTS Admin objects. You can set up Security administration in the AfterBuild event of a ProjectHook class you have tied to the project that generates your MTS COM DLL server. See the section "Using Visual FoxPro 6.0 Project Hooks" for examples.

The Basic Features of MTS

Before we jump right into using Visual FoxPro with MTS, let's review some basic concepts that you need to know in order to make effective use of the MTS environment. For more detailed information, see MTS Help.

Activity

An activity is a collection of MTS objects that has a single distributed thread of logical execution. Each MTS object belongs to a single activity. This is a basic concept that describes how the middle-tier functions when confined to the MTS environment. In an MTS package, multiple clients can access objects, but only one object per client is running at a time on a single thread.

Context

Context is state that is implicitly associated with a given MTS object. Context contains information about the object's execution environment, such as the identity of the object's creator and, optionally, the transaction encompassing the work of the object. The MTS run-time environment manages a context for each object.

As a developer, think of every Visual FoxPro object that is registered in an MTS package as having an associated Context object that is created every time you instantiate the Visual FoxPro object. So, each time you issue a CreateObject command, two objects are created—your server and its associated Context. In fact, you can return an object reference to this Context object directly in your code, as in the following example:

#DEFINE MTX_CLASS   "MTXAS.APPSERVER.1"
LOCAL oMTX,oContext
oMtx = CREATEOBJECT(MTX_CLASS)
oContext = oMtx.GetObjectContext()

The Context object has the following properties and methods.

Count CreateInstance DisableCommit
EnableCommit IsCallerInRole IsInTransaction
IsSecurityEnabled Item Security
SetAbort SetComplete  

As you can see, the properties, events, and methods (PEMs) are used to access information related to the object transaction and security context (see MTS Help for more details on specific syntax for these PEMs). It is important to understand that the Context state is inherited. An object in a package called from another object in the same package will inherit the state of its caller. Because Context is confined within the same process, state, such as security, is trusted. No object in a package needs to explicitly provide its own security. When your object is released, so is its Context.

Package

Packages, as we just described, are the building blocks of MTS. Think of them as mini applications—a set of components that perform related application functions. All components in a package run in the same MTS process.

Remember, "Good MTS applications are designed with MTS in mind from the start." You should design your Package contents with your entire application in mind. Each package runs in its own process, so try to design packages that don't attempt to do more than they absolutely need to. There are performance advantages to maintaining many components within in a single package, but there may also be security constraints (roles) that dictate a different architecture.

Packages are also the primary means of deployment. The MTS environment allows one to export the contents of a Package to a nice distributable setup (both client and server). We'll discuss this in the "Deployment" section.

Role

A role is a symbolic name that defines a class of users for a set of components. Each role defines which users are allowed to invoke interfaces on a component. A role is the primary mechanism to enforce security. Role-based security is handled at the component level. It's possible that this may be at the method level in a future version of MTS. Security cannot be enforced on the Windows 95 version of MTS.

Roles are stored at the package level. Each component in a package can belong to one of more of the defined roles. For example, an Inventory package might contain a Visual FoxPro server whose responsibility is to handle inventory. There are two roles defined in this package: Managers and Clerks. These two roles are simply collections of Windows NT users/groups with a collective name that you provide. Your server is coded so that Clerks can access inventory data for normal order entries and reporting. Managers have additional power in that they can override inventory levels to make adjustments (for example, quarterly product shrinkage estimates).

You can set up security so that it is automatically handled (for instance, users not in roles are given "Access is denied" error message), or you can manage it programmatically through code. The Context object's IsCallerInRole method is ideal for this.

Resource Dispensers

A resource dispenser manages nondurable shared state on behalf of the application components within a process. Resource dispensers are similar to resource managers, but without the guarantee of durability. MTS provides two resource dispensers:

  • The ODBC resource dispenser
  • The Shared Property Manager

Resources are shared within the same process—same process = same package. In the section "Shared Property Manager," we discuss programmatically accessing shared properties. This is a really cool thing for Visual FoxPro developers because it allows multiple instances of objects to share state information. For example, you could have a counter that tracks the last ID number used by a database.

ODBC resource dispenser

The ODBC resource dispenser manages pools of database connections for MTS components that use the standard ODBC interfaces, allocating connections to objects quickly and efficiently. Connections are automatically enlisted on an object's transactions and the resource dispenser can automatically reclaim and reuse connections. The ODBC 3.0 Driver Manager is the ODBC resource dispenser; the Driver Manager DLL is installed with MTS.

Shared Property Manager

The Shared Property Manager provides synchronized access to application-defined, process-wide properties (variables). For example, you can use it to maintain a Web page hit counter or to maintain the shared state for a multiuser game.

Resource Managers

A resource manager is a system service that manages durable data. Server applications use resource managers to maintain the durable state of the application, such as the record of inventory on hand, pending orders, and accounts receivable. Resource managers work in cooperation with the Microsoft Distributed Transaction Coordinator (MS DTC) to guarantee atomicity and isolation to an application. MTS supports resource managers, such as Microsoft SQL Server version 6.5, that implement the OLE Transactions protocol.

The MS DTC is a system service that coordinates transactions. Work can be committed as an atomic transaction even if it spans multiple resource managers, potentially on separate computers. MS DTC was first released as part of SQL Server 6.5 and is included in MTS, providing a low-level infrastructure for transactions. MS DTC implements a two-phase commit protocol to ensure that the transaction outcome (either commit or abort) is consistent across all resource managers involved in a transaction. MS DTC ensures atomicity, regardless of failures.

You might be asking if Visual FoxPro is a resource manager, because it has its own native database. Unfortunately, the answer is no. Visual FoxPro transactions are native to Visual FoxPro and do not go through the MS DTC. Therefore, automatic transaction support within MTS is not supported for Visual FoxPro data. You cannot use the Context object's SetAbort method to abort a transaction if the data is stored in Visual FoxPro databases/tables. The database must either support OLE Transactions (SQL Server) or be XA-compliant (Oracle).

Base Clients

A base client is simply a client that runs outside of the MTS run-time environment, but instantiates MTS objects. In a three-tier architecture, a base client is typically the presentation layer, such as an application form or Web page. The base client neither knows nor needs to know that MTS is used in the middle tier. It merely creates an instance of an object that exists in an MTS package and awaits a response. The following table describes some of the differences between a base client and an MTS component, such as a Visual FoxPro DLL server.

Base client MTS component
Can be EXEs, DLLs. Must be in-process DLL.
MTS does not manage its process. Manages server processes that host MTS component.s
MTS does not create or manage threads used by application. Creates and manages threads.
Does not have implicit Context object. Each MTS object has own Context object.
Cannot use Resource Dispensers. Can use Resource Dispensers.

Just-In-Time Activation

Just-in-Time (JIT) activation is the ability to activate an MTS object only as needed for executing requests from a client. Most Visual FoxPro developers are familiar with object instantiation, as in the following code:

myObject = CreateObject("myclass")
myObject.myMethod()
myObject.myProperty = 123
RELEASE myObject

A "stateful" object created by this code retains state during the lifetime of the object (until it is released). This means that property values (such as myProperty) are retained between statement execution. When the object is finally released, all object references and state are released.

There is overhead with creating objects from your Visual FoxPro components. Each time you instantiate an object, Visual FoxPro needs to allocate a certain amount of memory. In addition, the first time you create an object, Visual FoxPro takes a little extra time to load its run-time libraries. When the last instance is released, the entire Visual FoxPro run time is also released.

JIT activation addresses many of these memory issues that affect performance. The first thing JIT does is cache the server's run-time libraries in memory, even though no outstanding object references exist. The first time you instantiate a Visual FoxPro server that's in an MTS package, the Visual FoxPro run time loads the address space of the MTS process. When you release the object, MTS still keeps the libraries in memory for a specified amount of time. You can change this setting in the package's property sheet (default = 3 minutes). This saves having to reload the run time when the object count hits 0.

The main thing that JIT activation offers is ability to transform your object from "stateful" to "stateless" mode. In the preceding example, you can interpret a "stateless" object as one having the initial default settings. So, in the example, the value of myProperty would be reset to its original setting. A stateless object is managed by MTS and is very lightweight, so it consumes much less memory. The only thing keeping the stateless object alive is the object reference held onto by the client. Internally, MTS recycles threads consumed by stateful objects when they go stateless. When a method is invoked on that object, it then becomes stateful again on a thread that could be different from the one originally created on.

Putting your objects into a stateless mode is handled easily by the Context object. The following code illustrates putting an object in a stateless mode:

#DEFINE MTX_CLASS   "MTXAS.APPSERVER.1"
LOCAL oMTX,oContext
oMtx = CREATEOBJECT(MTX_CLASS)
oContext = oMtx.GetObjectContext()
oContext.SetComplete()

This code is actually called from within a method of your Visual FoxPro server. You can see if your object is stateless by viewing the status of your component in the MTS Explorer. A stateless object appears in the Objects column, but not in the Activated or In Call columns.

Use the SetComplete method to put the object in a stateless mode. Use SetComplete for committing transactions (as we discuss in the next section, "Transactions"). You can also use SetAbort to make an object stateless.

Again, when you change an object to stateless, all property settings revert to their original defaults. When you invoke a method (or property set/get) on this stateless object, the object is activated (goes stateful) and the object's INIT event is fired. When you call SetComplete, the object DESTROY event is fired.

Note   Any state that exists on the object is lost when the object is deactivated (SetComplete). If you need to save state, you should either persist information to a database or use the MTS Shared Property Manager.

Because your object's INIT is called whenever your object goes from Stateless to Stateful, you should try to minimize the amount of code in this event.

Here is a simple scenario showing interaction between client and MTS server.

Visual FoxPro server code:

DEFINE CLASS mts2 AS Custom OLEPUBLIC
   MyColor = "Green"
   PROCEDURE InUsa (tcCustID)
      LOCAL llInUSA,oMTX,oContext
      oMtx = CreateObject("MTXAS.APPSERVER.1")
      oContext = oMtx.GetObjectContext()
      llInUSA = .F.
      USE CUSTOMER AGAIN SHARED
      LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)
      IF FOUND()
         llInUSA = (ATC("USA",country)#0)
      ENDIF
      oContext.SetComplete()
      RETURN llInUSA
   ENDPROC
ENDDEFINE

Base client executes following code:

LOCAL oCust,cCust,lUsa
oCust = CreateObject("vfp_mts.mts2")
? oCust.MyColor
Green
oCust.MyColor = "Red"
? oCust.MyColor
Red
cCust = "JONES"
lUsa = oCust.InUsa(cCust)   && object goes stateless (deactivated)
? oCust.MyColor      && object is activated (stateful)
Green
RELEASE oCust         && object is fully released

Notice in the preceding example how the state of oCust is lost after the InUsa method is called. The MyColor property no longer returns Red, but is instead reset to its original value of Green.

Transactions

If you have used Visual FoxPro at all, you are probably aware that Visual FoxPro supports transactions. Changes to your data can be committed or rolled back. Though transactions are critical to MTS, don't be misled by the name; there is a lot more to it than just transactions. However, the ability to have MTS automatically handle transactions between distributed objects is quite powerful. Transactions are often discussed in terms of the ACID acronym:

  • Atomicity—ensures that either the entire transaction commits or nothing commits.
  • Consistency—a transaction is a correct transformation of the system state.
  • Isolation—protects concurrent transactions from seeing each other's partial and uncommitted results.
  • Durability—committed updates to managed resources can survive failures.

As just mentioned, MTS transaction support is not compatible with Visual FoxPro data. It only works with databases supporting OLE transaction or XA protocols. Both SQL Server and Oracle data can be used with MTS in transactional fashion.

You should understand what we mean by a transaction and to what extent things are either committed or rolled back. Consider the following scenario (all done within confines of two components in a single MTS package):

  1. Component A adds a new customer record to the Customer table in SQL Server.
  2. Component A writes out new record to a Visual FoxPro database (audit log).
  3. Component A sends e-mail notification of new customer to some manager.
  4. Component A calls Component B.
  5. Component B edits the Orders table with a new order in SQL Server.
  6. Component B writes out text log file of activity.
  7. Component B completes activity by committing the transaction (SetComplete).
  8. Component A discovers bad credit history with customer and aborts transaction (SetAbort).

When Component B commits in step 7, not a whole lot happens because MTS manages the entire Context within the package in a distributed fashion. Component B actually inherits transaction state from Component A, so it cannot really fully commit the transaction. The real transaction terminates in step 8 when the last object with transaction state aborts. At this point, changes made to both Customer and Orders tables are rolled back because these tables are in SQL Server. Unfortunately, the Visual FoxPro table update, e-mail notification, and text log file activities are not rolled back. When a transaction is aborted/committed, only data managed through the MS DTC is affected. There is no event that is magically triggered. (Check out the MTS SDK for ideas on using Spy).

Remember, good MTS apps are written with MTS in mind from the start. Managing transactions is very important, and while much of it is handled automatically, you will need to provide a fair amount of code to effectively manage all the resources being utilized in a transaction setting.

Transaction support is set at the component level, but transactions can span multiple packages. You can set this option in the MTS Explorer from the component's Property Sheet (see MTS Help for details on the various options). Again, the object's Context manages and passes on transaction state for a given component. If the transaction setting of a component is marked as "Requires a transaction," a transaction is always associated with the component. If another object that calls this component already has a transaction in effect, no new transaction is created. The component merely inherits the current one. A new transaction is only created if one does not already exist in the context.

Figure 3. Setting Transaction support

Let's return a minute to the SetComplete and SetAbort methods. These methods actually serve two purposes. From their names, they imply functionality related to transactions. However, as already discussed, they also serve to deactivate objects (make them stateless). In fact, these methods can be used simply for JIT activation without any concern for transactional support. Again, SetComplete releases valuable resources/memory used by MTS to allow for improved scalability. The Context object also includes several other methods useful for transactions: EnableCommit, DisableCommit, and IsInTransaction. The following example shows how to handle transactions in Visual FoxPro:

LPARAMETER tcCustID
LOCAL lFound,oMTX,oContext
oMtx = CreateObject("MTXAS.APPSERVER.1")
oContext = oMtx.GetObjectContext()
USE CUSTOMER AGAIN SHARED
LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)
lFound = FOUND()
IF FOUND()
oContext.SetComplete()
ELSE
oContext.SetAbort()
ENDIF
RETURN lFound

In this scenario, we assume that another component already performed an update on another table (for example, Orders). If the customer ID in the preceding code was not found, the entire transaction would be rolled back.

You're probably wondering how transactions work in the code, which clearly appears to be against Visual FoxPro data. Actually, this example is using Remote Views against SQL Server data. Again, Visual FoxPro tables do not support OLE transactions, so you will not get MTS transaction support if you use DBF tables. However, data updates either to Remote Views or by SQL pass-through work just fine.

**Tip   **Make sure that your connection to a remote data source is made without any login dialog box. If you are using a connection stored in a DBC, ensure that the Display ODBC logins prompt is set to Never. For access to remote data through SQL pass-through commands, you can use the SQLSetProp function:

 SQLSETPROP(0, 'DispLogin', 3)

Programming Models

MTS supports two programming models. The TransactionContext model is intended primarily for backward compatibility. It essentially lets the base client control the transaction. The assumption is that the COM component has no MTS awareness (that is, the component was written before MTS was available). The second model is called the ObjectContext model and assumes the COM component inside the MTS package has MTS smarts and is aware of its Context object.

TransactionContext

We do not recommend using this model for new three-tier applications, because it has limited access to the full capabilities of MTS. It merely offers a way to provide some transaction support to applications whose middle-tier components were developed without MTS in mind. The burden of transaction handling rests on the base client. With this model, the base client is likely to be a smart client that has scripting capabilities (for example, an application form). The base client is less likely to be a Web page, and it always runs outside of the MTS run-time environment.

The following code snippet in a Visual FoxPro form (base client) shows this model in use. The middle-tier component is a Visual FoxPro server whose ProgID is "vfp_mts.mts1". The assumption here is that this server knows nothing about MTS, thus requiring the base client to perform all transaction handling:

#DEFINE TRANS_CLASS   "TxCtx.TransactionContext"
THIS.oContext = CreateObject(TRANS_CLASS)
LOCAL loCust
loCust = THISFORM.oContext.CreateInstance("vfp_mts.mts1")
RETURN loCust.lnUSA

The code in the middle tier simply does a lookup in a SQL Server table for a customer's home country. If the record was actually changed, the base client would have the capability to actually commit or roll back the transaction. The TransactionContext object only supports three methods: CreateInstance, Commit, and Abort.

ObjectContext

The ObjectContext model is the only model you should consider for new MTS application development. It relies on component awareness of MTS, but this should be your goal so that you can optimize performance and take advantage of MTS-specific features.

Unlike the TransactionContext object, which uses the following PROGID:

#DEFINE TRANS_CLASS   "TxCtx.TransactionContext"

the ObjectContext object can be accessed using the following code:

#DEFINE MTX_CLASS   "Mtxas.AppServer.1"

The ObjectContext object, which can be referenced in your Visual FoxPro code, as shown here:

LOCAL oMTX,oContext
oMtx = CreateObject("MTXAS.APPSERVER.1")
oContext = oMtx.GetObjectContext()

contains the following properties, events, and methods (PEMs).

PEM Description
Count Returns the number of Context object properties.
CreateInstance Instantiates another MTS object.
DisableCommit Declares that the object hasn't finished its work and that its transactional updates are in an inconsistent state. The object retains its state across method calls, and any attempts to commit the transaction before the object calls EnableCommit or SetComplete will result in the transaction being aborted.
EnableCommit Declares that the object's work isn't necessarily finished, but its transactional updates are in a consistent state. This method allows the transaction to be committed, but the object retains its state across method calls until it calls SetComplete or SetAbort, or until the transaction is completed.
IsCallerInRole Indicates whether the object's direct caller is in a specified role (either directly or as part of a group).
IsInTransaction Indicates whether the object is executing within a transaction.
IsSecurityEnabled Indicates whether security is enabled. MTS security is enabled unless the object is running in the client's process.
Item Returns a Context object property.
Security Returns a reference to an object's SecurityProperty object.
SetAbort Declares that the object has completed its work and can be deactivated on returning from the currently executing method, but that its transactional updates are in an inconsistent state or that an unrecoverable error occurred. This means that the transaction in which the object was executing must be aborted. If any object executing within a transaction returns to its client after calling SetAbort, the entire transaction is doomed to abort.
SetComplete Declares that the object has completed its work and can be deactivated on returning from the currently executing method. For objects that are executing within the scope of a transaction, it also indicates that the object's transactional updates can be committed. When an object that is the root of a transaction calls SetComplete, MTS attempts to commit the transaction on return from the current method.

Deployment

Microsoft Transaction Server offers excellent tools for deploying both client- and server-side setups. Setups are made at the package level, so you should include all components for your application in a particular package. The deployment package contains all the distributed COM (DCOM) configuration settings you need, so you don't have to fuss with the messy DCOM Configuration dialog box.

To create a setup

  1. Click the package that you want to create setup.
  2. Select Export… from the Action menu. The Export dialog box is displayed.

Figure 4. Exporting a package

**Important   **The directions in the Export dialog box are not very clear. You should not simply type in a path as specified. If you do, the Export routine creates a file with a .pak extension in the folder location you specify. Instead, you should always type a full path and file name for the .pak file, as shown in Figure 4.

You can also use the scriptable administration objects to automate deployment and distribution of your MTS packages. See the section "Remote Deployment and Administration" to follow for more details.

The output of the Export operation consists of two setups:

Server Setup

This setup, which is placed in the folder specified in the Export dialog box, contains the .pak file and all COM DLL servers used by the package.

Note   With Visual FoxPro servers, you will also have .tlb (type library) files included. You can install this package by selecting Install from the Package Wizard in MTS Explorer.

Figure 5. Installing package from the Package Wizard

Client Setup

The Export process creates a separate subfolder named "clients" in the folder specified in the Export Package dialog box. The Clients folder contains a single .exe file that a user can double-click to run.

The Client setup merely installs necessary files and registry keys needed by a client to access (remotely through DCOM) your MTS package and its COM servers.

Remote Deployment and Administration

The MTS Explorer allows you to manage remote components (those installed on a remote machine). The Remote Components folder contains the components that are registered locally on your local computer to run remotely on another computer. Using the Remote Components folder requires that you have MTS installed on the client machines that you want to configure. If you want to configure remote computers manually using the Explorer, add the components that will be accessed by remote computers to the Remote Components folder.

Pushing and Pulling

If both the server and client computer are running MTS, you can distribute a package by "pulling" and "pushing" components between one or more computers. You can "push" components by creating remote component entries on remote computers and "pull" components by adding component entries to your local computer. Once you create the remote component entries, you must add those component entries to your Remote Components folder on your local machine (pull the components).

Before you deploy and administer packages, set your MTS server up by doing the following:

  • Configure roles and package identity on the system package.
  • Set up computers to administer.

You must map the System Package Administrator role to the appropriate user in order to safely deploy and manage MTS packages. When MTS is installed, the system package does not have any users mapped to the administrator role. Therefore, security on the system package is disabled, and any user can use the MTS Explorer to modify package configuration on that computer. If you map users to system package roles, MTS will check roles when a user attempts to modify packages in the MTS Explorer.

Roles

By default, the system package has an Administrator role and a Reader role. Users mapped to the Administrator role of the system package can use any MTS Explorer function. Users that are mapped to the Reader role can view all objects in the MTS Explorer hierarchy but cannot install, create, change, or delete any objects, shut down server processes, or export packages. If you map your Windows NT domain user name to the System Package Administrator role, you will be able to add, modify, or delete any package in the MTS Explorer. If MTS is installed on a server whose role is a primary or backup domain controller, a user must be a domain administrator in order to manage packages in the MTS Explorer.

You can also set up new roles for the system package. For example, you can configure a Developer role that allows users to install and run packages, but not delete or export them. The Windows NT user accounts or groups that you map to that role will be able to test installation of packages on that computer without having full administrative privileges over the computer.

In order to work with a remote computer, you first need to add it to the Computers folder in the MTS Explorer:

  1. Click the Computers folder.
  2. Select New -> Computer from the Action menu.
  3. Enter name of the remote computer.

Important   You must be mapped to the Administrator role on the remote computer in order to access it from your machine. In addition, you cannot remotely administer MTS on a Windows 95 computer from MTS on a Windows NT server.

You should now see both My Computer and the new remote computer under the Computers folder. At this point, you can push and pull components between the two machines. Think of the Remote Components folder as its own special package. You are merely adding to it components that exist in one or more packages of remote machines.

The following example pulls a component from a remote machine to My Computer.

  1. Click the Remote Components folder of My Computer.
  2. Select New-> Remote Component from the Action menu to display the dialog box shown here.

Figure 6. Adding a component to Remote Components

In this example, we select (and add) a component called test6.foobar2 from a package called aa on the remote machine calvinh5. This package also has another component (Visual FoxPro OLEPUBLIC class) named test6.foobar, which we do not select. When we click OK, a copy of the DLL and the type library are copied to the local machine (My Computer) and stored in a subfolder of your MTS root location (in this case, c:\ C:\Program Files\Mts\Remote\aa\). In addition, the server is now registered on your machine. Note that while the DLL is copied to your machine, the .dll registered in your registry points to the remote machine.

If you encounter problems after you click OK, you may not have proper access rights to copy the server components. Ensure that the remote machine is configured with proper access privileges for you. At this point, you can go into Visual FoxPro running on the local machine and access the server:

oServer = CreateObject("test6.foobar2")
? oServer.myeval("SYS(0)")

You use MTS Explorer to view the activated object in the remote machine folder under the package it is registered in. You will not see the object activity in the Remote Components folder. See the "Working with Remote MTS Computers" topic in the MTS Help file for more details.

Security

Security in MTS is handled by roles. Roles are established at the package level. Components within that package can set up role memberships. The following MTS Explorer image shows a package called Devcon1, which contains three roles. Only the last two components contain Role Memberships.

Figure 7. Package with roles

If you navigate the Roles folder, you can see all Windows NT users or groups assigned to that particular role.

To create a new role

  1. Click the Roles folder.
  2. Select New-> Role from the Action menu.
  3. Enter a new role name in the dialog box.

You can add new users/groups to a particular role as follows:

To add new users or groups

  1. Click the Users folder of the newly added role.
  2. Select New-> User from the Action menu.
  3. Select users/groups from the dialog box.

MTS handles its security several different ways. The MTS security model consists of declarative security and programmatic security. Developers can build both declarative and programmatic security into their components prior to deploying them on a Windows NT security domain.

You can administer package security using MTS Explorer. This form of declarative security, which does not require any component programming, is based on standard Windows NT security. This can be done by Package- or Component-level security.

Declarative Security

You can manage Declarative security at the package and at the component level through settings available in the Security tab of the Package Properties dialog box.

Package-level security

Each package has its own security access authorization, which can be set in the Package Properties dialog box.

Figure 8. Package properties

By default, the Security check box is not marked, so you need to check this box to enable security. If you do not enable security for the package, MTS will not check roles for the component. If security is enabled, you must also enable security at the component level in order to have roles checked.

Component-level security

Each installed component can also have its own security setting. You set security for a component through the same Enable authorization checking check box on the Property dialog box in MTS Explorer. If you are enabling security at both levels and you do have defined roles, you must include one of the roles in the component's Role Membership folder. If you do not include a role in the folder, you will get an "Access is denied" error message when you try to access a property or method of the component. Of course, if you do not have any roles, you will get the same error.

Note   You can still do a CreateObject on the component, but that is all.

oContext = CreateObject("vfp_mts.mts1")
oContext.Hello()   && will generate an Access is denied error

To restrict access to a specific component within a package, you must understand how components in the package call one another. If a component is directly called by a base client, MTS checks roles for the component. If one component calls another component in the same package, MTS does not check roles because components within the same package are assumed to "trust" one another.

When you change the security settings for a particular package or component, you need to shut down server processes before changes can take place. This option is available from the Action menu when Package is selected.

Programmatic Security

You can put code in your program to check for specific security access rights. The following three properties and methods from the Context object return information regarding security for that package or component.

Methods Description
IsCallerInRole Indicates whether the object's direct caller is in a specified role (either directly or as part of a group).
IsSecurityEnabled Indicates whether security is enabled. MTS security is enabled unless the object is running in the client's process.
Security Returns a reference to an object's SecurityProperty object.

The following method checks whether the called object is in a particular role. The IsCallerInRole method is useful when the roles are defined, but if your code is generic and doesn't know the particular roles associated with a component, you must handle this through your error routine.

PROCEDURE GetRole (tcRole)   
   LOCAL oMTX,oContext,lSecurity,cRole,lHasRole
   IF EMPTY(tcRole)
      RETURN "No Role"
   ENDIF
   oMtx = CREATEOBJECT(MTX_CLASS)
   oContext = oMtx.GetObjectContext()
   IF oContext.IsSecurityEnabled
      THIS.SkipError=.T.
      lHasRole = oContext.IsCallerInRole(tcRole)
      THIS.SkipError=.F.
      DO CASE
      CASE THIS.HadError
         THIS.HadError = .F.
         cRole="Bad Role"
      CASE lHasRole 
         cRole="Yep"
      OTHERWISE
         cRole="Nope"
      ENDCASE
ELSE
      cRole="No Security"
ENDIF
   oContext.SetComplete()
   RETURN cRole
ENDPROC

Advanced users can access the SecurityProperty object to obtain more details on the user for handling security. The Security object offers the following additional methods.

Method Description
GetDirectCallerName Retrieves the user name associated with the external process that called the currently executing method.
GetDirectCreatorName Retrieves the user name associated with the external process that directly created the current object.
GetOriginalCallerName Retrieves the user name associated with the base process that initiated the call sequence from which the current method was called.
GetOriginalCreatorName Retrieves the user name associated with the base process that initiated the activity in which the current object is executing.

What type of security should you use? Programmatic security offers more power in terms of structuring specific functionality for particular roles. You can use Case statements, as in the previous example, which perform different tasks, depending on the role. Declarative security, on the other hand, can only control access at the component level (not method or lower).

Changes to Programmatic security, however, require a new build of the component, which may not always be convenient or realistic. Controlling Component-level security for users and roles by using MTS Explorer to turn security on or off gives an administrator greater control. The optimal solution is one with utilizes both declarative and programmatic securities in the most efficient manner.

Shared Property Manager

The Shared Property Manager (SPM) MTS resource dispenser allows you to create and share properties across components. Because it is a resource dispenser, all other components in the same package can share information, but information cannot be shared across different packages. For example, if you want to keep a counter to use for generating unique IDs for objects in a package, you could create a Counter property to hold the latest unique ID value. This property would be preserved while the package was active (regardless of object state).

The SPM also represents an excellent way for an object to preserve its state before being deactivated in a stateless mode (SetComplete). Just-In-Time activation does not affect or reset the state of SPM.

The following example shows how to use the SPM with Visual FoxPro servers:

#DEFINE MTX_CLASS        "MTXAS.APPSERVER.1"
#DEFINE MTX_SHAREDPROPGRPMGR "MTxSpm.SharedPropertyGroupManager.1"
PROCEDURE GetCount (lReset)
   LOCAL oCount 
   LOCAL oMTX,oContext
   LOCAL nIsolationMode,nReleaseMode,lExists
   oMtx = CREATEOBJECT(MTX_CLASS)
   oContext = oMtx.GetObjectContext()
   oSGM = oContext.CreateInstance(MTX_SHAREDPROPGRPMGR)
   nIsolationMode = 0
   nReleaseMode = 1
   
* Get group reference in which property is contained
   oSG = oSGM.CreatePropertyGroup("CounterGroup", nIsolationMode,;
nReleaseMode, @lExists)
   
* Get object reference to shared property
   oCount = oSG.CreateProperty("nCount", @lExists)
* check if property already exists otherwise reset
   IF lReset OR !lExists
      oCount.Value = 1
   ELSE
      oCount.Value = oCount.Value + 1
   ENDIF
   RETURN oCount.Value
ENDPROC

The following settings are available for Isolation and Release modes.

Isolation mode

LockSetGet 0 (default)—Locks a property during a Value call, assuring that every get or set operation on a shared property is atomic. This ensures that two clients can't read or write to the same property at the same time, but doesn't prevent other clients from concurrently accessing other properties in the same group.

LockMethod 1—Locks all of the properties in the shared property group for exclusive use by the caller as long as the caller's current method is executing. This is the appropriate mode to use when there are interdependencies among properties or in cases where a client may have to update a property immediately after reading it before it can be accessed again.

Release mode

Standard 0 (default)—When all clients have released their references on the property group, the property group is automatically destroyed.

Process 1—The property group isn't destroyed until the process in which it was created has terminated. You must still release all SharedPropertyGroup objects by setting them to Nothing.

MTS Support for Internet Information Server

MTS includes several special system packages for use with Microsoft Internet Information Server (IIS). The Windows NT Options Pack 4.0 integrates MTS and IIS more closely. In the future, you can expect even better integration to play a more central role in your Web applications.

IIS Support

  • Transactional Active Server Pages—You can now run Scripts in Active Server Pages (ASP) within an MTS-managed transaction. This extends the benefits of MTS transaction protection to the entire Web application.
  • Crash Protection for IIS Applications—IIS Web applications can now run within their own MTS package, providing process isolation and crash protection for Web applications.
  • Transactional Events—You can embed commands in scripts on ASP pages, enabling you to customize Web application response based on transaction results.
  • Object Context for IIS Built-In Objects—The MTS object context mechanism, which masks the complexity of tracking user state information from the application developer, now tracks state information managed by IIS built-in objects. This extends the simplicity of the MTS programming model to Web developers.
  • Common Installation and Management—MTS and IIS now share common installation and a common management console, lowering the complexity of deploying and managing business applications on the Web.

IIS System Packages

If you use MTS with Internet Information Server version 4.0, the Packages Installed folder contains the following IIS-specific system packages.

IIS in-process applications

The IIS In-Process Applications folder contains the components for each Internet Information Server application running in the IIS process. An IIS application can run in the IIS process or in a separate application process. If an IIS application is running in the IIS process, the IIS application will appear as a component in the IIS In-Process Applications folder. If the IIS application is running in an individual application process, the IIS application will appear as a separate package in the MTS Explorer hierarchy.

IIS utilities

The IIS Utilities Folder contains the ObjectContext component required to enable transactions in ASP pages. For more information about transactional ASP pages, refer to the Internet Information Server documentation.

Automating MTS Administration

Microsoft Transaction Server contains Automation objects that you can use to program administrative and deployment procedures, including:

  • Installing a prebuilt package.
  • Creating a new package and installing components.
  • Enumerating through installed packages to update properties.
  • Enumerating through installed packages to delete a package.
  • Enumerating through installed components to delete a component.
  • Accessing related collection names.
  • Accessing property information.
  • Configuring a role.
  • Exporting a package.
  • Configuring a client to use Remote Components.

You can use the following Admin objects in your Visual FoxPro code.

Object Description
Catalog The Catalog object enables you to connect to MTS Catalog and Access collections.
CatalogObject The CatalogObject object allows you to get and set object properties.
CatalogCollection Use the CatalogCollection object to enumerate, add, delete, and modify Catalog objects and to access related collections.
PackageUtil The PackageUtil object enables installing and exporting a package. Instantiate this object by calling GetUtilInterface on a Packages collection.
ComponentUtil Call the ComponentUtil object to install a component in a specific collection and import components registered as in-process servers. Create this object by calling GetUtilInterface on a ComponentsInPackage collection.
RemoteComponentUtil Using the RemoteComponentUtil object, you can program your application to pull remote components from a package on a remote server. Instantiate this object by calling GetUtilInterface on a RemoteComponents collection.
RoleAssociationUtil Call methods on the RoleAssociationUtil object to associate roles with a component or interface. Create this object by calling the GetUtilInterface method on a RolesForPackageComponent or RolesForPackageComponentInterface collection.

In addition, the following collections are also supported.

Collection
LocalComputer
ComputerList
Packages
ComponentsInPackage
RemoteComponents
InterfacesForComponent
InterfacesForRemoteComponent
RolesForPackageComponent
RolesForPackageComponentInterface
MethodsForInterface
RolesInPackage
UsersInRole
ErrorInfo
PropertyInfo
RelatedCollectionInfo

If you want to get a reference to a particular collection, use the GetCollection method. The following example shows, first, getting the collection of packages and, second, getting a collection of all components in the first package:

#DEFINE MTS_CATALOG      "MTSAdmin.Catalog.1"
oCatalog = CreateObject(MTS_CATALOG)
oPackages = oCatalog.GetCollection("Packages")
oPackages.populate()
? oPackages.Count
oComps = oPackages.GetCollection("ComponentsInPackage",;
oPackages.Item(0).Key)
oComps.Populate()

Note   The GetCollection method merely returns an object reference to an empty collection. You need to explicitly call the Populate method to fill the collection.

Collections are case sensitive, as in the following example code:

oPackages = oCatalog.GetCollection("Localcomputer")   &&fails
oPackages = oCatalog.GetCollection("LocalComputer")   &&works

Note   Also keep in mind that all MTS collections are zero-based.

oPackages = oCatalog.GetCollection("LocalComputer")
oPackages.populate()
? oPackages.item[0].name

See MTS Help for more specific language details.

Visual FoxPro 6.0 is ideally suited for using MTS Automation because of the new Project Manager and Application Builder hooks support.

Using Visual FoxPro 6.0 Project Hooks

The MTS samples posted along with this document contain a special Project Hook class designed specially for MTS. This class automatically shuts down and refreshes MTS registered servers contained in that project. One of the issues that developers must consider when coding and testing servers under MTS is repeatedly opening the MTS Explorer to manually shut down processes so that servers can be rebuilt and overwritten. Using a Project Hook nicely automates this process. Here is sample code from the BeforeBuild event, which iterates through the Packages collection shutting-down processes.

* BeforeBuild event
LPARAMETERS cOutputName, nBuildAction, lRebuildAll, lShowErrors, lBuildNewGuids
#DEFINE MTS_CATALOG      "MTSAdmin.Catalog.1"
#DEFINE   MSG_MTSCHECK_LOC   "Shutting down MTS servers...."
LOCAL oCatalog,oPackages,oUtil,i,j,oComps
LOCAL oProject,lnServers,laProgIds,lcSaveExact
THIS.lBuildNewGuids = lBuildNewGuids
oProject = _VFP.ActiveProject
lnServers = oProject.servers.count
DIMENSION THIS.aServerInfo[1]
STORE "" TO THIS.aServerInfo
IF lnServers = 0 OR nBuildAction # 4
   RETURN
ENDIF
WAIT WINDOW MSG_MTSCHECK_LOC NOWAIT
DIMENSION laProgIds[lnServers,3]
FOR i = 1 TO lnServers
   laProgIds[m.i,1] = oProject.servers[m.i].progID
   laProgIds[m.i,2] = oProject.servers[m.i].CLSID
   laProgIds[m.i,3] = THIS.GetLocalServer(laProgIds[m.i,2])
ENDFOR
ACOPY(laProgIds,THIS.aServerInfo)
* Shutdown servers
oCatalog = CreateObject(MTS_CATALOG)
oPackages = oCatalog.GetCollection("Packages")
oUtil = oPackages.GetUtilInterface
oPackages.Populate()
lcSaveExact = SET("EXACT")
SET EXACT ON
FOR i = 0 TO oPackages.Count - 1
   oComps = oPackages.GetCollection("ComponentsInPackage",;
oPackages.Item(m.i).Key)
   oComps.Populate()
   FOR j = 0 TO oComps.Count-1
IF ASCAN(laProgIds,oComps.Item(m.j).Value("ProgID")) # 0
oUtil.ShutdownPackage(oPackages.Item(m.i).Value("ID"))
EXIT
ENDIF
   ENDFOR
ENDFOR
WAIT CLEAR
SET EXACT &lcSaveExact
* User is building new GUIDs, so packages 
* need to be reinstalled manually
IF lBuildNewGuids
   RETURN
ENDIF

This is only one of the many possibilities provided by a Visual FoxPro Project Hook. The MTS Admin objects can save a great deal of time you normally would spend manually setting options in the MTS Explorer.

Using Visual FoxPro 6.0 Application Builders

As with the Project Hooks, you might also want to create an Application (Project) Builder that handles registration of Visual FoxPro Servers in MTS packages. The Visual FoxPro MTS samples include such a builder. (See the Readme file in the mtsvfpsample sample application for more details on setup and usage of these files.)

This Builder simply enumerates through all the servers in your Visual FoxPro project and all the available MTS packages. You can then select (or create) a particular package and registered server to install in that package. Additionally, you can set the Transaction property for each component. The Visual FoxPro code called when the user clicks OK is as follows:

#DEFINE   MTS_CATALOG      "MTSAdmin.Catalog.1"
#DEFINE   ERR_NOACTION_LOC   "No action taken."
LOCAL oCatalog,oPackages,oUtil,i,j,oComps,nPoslcPackage
LOCAL lPackageExists,oCompRef
LOCAL oProject,lnServers,laProgIds,lcSaveExact,oPackageRef,lctrans
lcPackage = ALLTRIM(THIS.cboPackages.DisplayValue)
lPackageExists = .f.
SELECT mtssvrs
LOCATE FOR include
IF !FOUND() OR EMPTY(lcPackage)
   MESSAGEBOX(ERR_NOACTION_LOC)
   RETURN
ENDIF
THIS.Hide
oCatalog = CreateObject(MTS_CATALOG)
oPackages = oCatalog.GetCollection("Packages")
oPackages.Populate()
FOR i = 0 TO oPackages.Count-1
   IF UPPER(oPackages.Item(m.i).Name) == UPPER(lcPackage)
      oPackageRef = oPackages.Item(m.i)
      lPackageExists=.T.
      EXIT
   ENDIF
ENDFOR
IF !lPackageExists   &&creating new package
   oPackageRef = oPackages.Add
   oPackageRef.Value("Name") = lcPackage
   oPackages.SaveChanges
ENDIF
oComps = oPackages.GetCollection("ComponentsInPackage",;
oPackageRef.Key)
oUtil = oComps.GetUtilInterface
SCAN FOR include
   oUtil.ImportComponentByName(ALLTRIM(progid))
ENDSCAN
oPackages.SaveChanges()
oComps.Populate()
SCAN FOR include
   DO CASE
   CASE trans = 1
      lctrans = "Supported"
   CASE trans = 2
      lctrans = "Required"
   CASE trans = 3
      lctrans = "Requires New"
   OTHERWISE
      lctrans = "Not Supported"         
   ENDCASE
   FOR j = 0 TO oComps.Count-1
      IF oComps.Item(m.j).Value("ProgID")=ALLTRIM(progid)
         oCompRef = oComps.Item(m.j)
         oCompRef.Value("Transaction") = lctrans
         oCompRef.Value("SecurityEnabled") = ;
IIF(THIS.chkSecurity.Value,"Y","N")
      ENDIF
   ENDFOR
ENDSCAN
oComps.SaveChanges()
oPackages.SaveChanges()

Tips and Tricks

Hopefully, this article offers enough insight into creating Visual FoxPro components that work well with your three-tier MTS applications. Here are a few final tips to consider:

  • Design your components with MTS in mind from the start.
  • Components must be in-process DLLs. Do not use Visual FoxPro EXE servers.
  • When adding Visual FoxPro components, make sure to select both .dll and .tlb files.
  • In the Project Info dialog box of Visual FoxPro DLL servers, set Instancing to MultiUse.
  • Don't be afraid to mix with other components (for example, Visual Basic servers).
  • You must have DTC running for transaction support.
  • Call SetComplete regardless of whether you're using transactions, because it places objects in stateless mode.
  • Your MTS object has an associated Context object. Do not place this code in the base client.
  • Connections must have DispLogin set to Never; for SQL pass-through, use SQLSetProp(0).
  • Minimize the number of PEMs on an object (protect your PEMs).
  • Because of page locking issues, limit the length of time you leave SQL Server 6.5 transactions uncommitted.
  • To use security, you must have a valid role associated with the component.
  • Avoid using CreateInstance on non-MTS components.
  • Do not pass object references of the Context object outside of the object itself.
  • Consider using disconnected ADO recordsets to move data between tiers.
  • You can pass Visual FoxPro data in strings, arrays, or ADO recordsets.
  • Passing Parameters:
    • Be careful when passing parameters.

    • Always use SafeArray when passing object references.

    • Passing by value:

      - Fastest and most efficient

      - Copies the parameters into a buffer

      - Sends all values at once

    • Passing by reference:

      - Sends a reference, but leaves the object back in the client.

      - Accessing the parameter scampers back to the client machine.

  • Always read the Late Breaking News! It contains important information such as Security configuration details.
  • Visit the Microsoft MTS Web site at www.microsoft.com/com/ for more information.
  • By default, MTS will create a maximum of 100 apartment threads for client work (per package). In Windows NT 4.0 Service Pack 4 (and later), you can tune the MTS activity thread pool. This will not affect the number of objects than can be created. It will simply configure the number that can be simultaneously in call. To tune the MTS activity thread pool:
    1. Open your Windows Registry using RegEdit and go to the package key:

      HKLM/Software/Microsoft/Transaction Server/Package/{your package GUID}

    2. Add a REG_DWORD named value:

      ThreadPoolMax

    3. Enter a value for ThreadPoolMax. Valid values are:

      0 to 0x7FFFFFFF

© Microsoft Corporation. All rights reserved.