Chapter 51
Creating MTS objects

MTS is a robust runtime environment that provides transaction services, security, and resource pooling for distributed COM applications.

Delphi provides an MTS Object wizard that creates an MTS object so that you can create server components that can take advantage of the benefits of the MTS environment. MTS provides many underlying services to make creating COM clients and servers, particularly remote servers, easier to implement.

MTS components provide a number of low-level services, such as

By providing these underlying services, MTS allows you to concentrate on developing the specifics for your particular distributed application. With MTS, you implement your business logic into MTS objects, or in MTS remote data modules. Upon building the components into libraries (DLLs), the DLLs are installed in the MTS runtime environment.

With Delphi, MTS clients can be stand-alone applications or ActiveForms. Any COM server can run within the MTS runtime environment.

This chapter provides an overview of the Microsoft Transaction Server (MTS) technology and how you can use it to write applications based on MTS objects. Delphi also provides support for an MTS remote data module, which is described in "Creating multi-tiered applications."

Microsoft Transaction Server components

MTS components are COM in-process server components contained in dynamic-link libraries (DLLs). They are distinguished from other COM components in that they execute in the MTS runtime environment. You can create and implement these components with Delphi or any ActiveX-compatible development tool.

Note: In MTS terms, a component represents the code that implements a COM object. For example, MTS components are implemented as classes in Delphi. MTS use of the term component interferes with Delphi traditional use of this term. We use component to refer to a class or object descending from the specific class, TComponent. However, to remain consistent with MTS terminology, we will use MTS component when talking specifically about MTS classes. In both MTS and Delphi, we use the term object to refer to an instance of an MTS component.

Typically, MTS server objects are small, and are used for discrete business functions. For example, MTS components can implement an application's business rules, providing views and transformations of the application state. Consider, for example, the case of a physician's medical application. Medical records stored in various databases represent the persistent state of the medical application, such as a patient's health history. MTS components update that state to reflect such changes as new patients, blood test results, and X-ray files.

As shown in Figure 51.1, an MTS object can be viewed as any other COM object. In addition to supporting any number of COM interfaces, it also supports MTS interfaces. Just as IUnknown is the interface common to all COM objects, IObjectControl is common to all MTS objects. IObjectControl contains methods to activate and deactivate the MTS object and to handle resources such as database connections.

Figure 51.1   MTS object interface

A client of a server within the MTS environment is called a base client. From a base client's perspective, a COM object within the MTS environment looks like any other COM object. The MTS object is installed as a DLL into the MTS executive. By running with the MTS EXE, MTS objects benefit from the MTS runtime environment features such as resource pooling and transaction support.

The MTS executive (.EXE) can be running in the same process as the base client as shown in Figure 51.2.

Figure 51.2   MTS In-process component

The MTS component can be installed in a remote server process within the same machine as shown in Figure 51.3. The base client talks to a proxy which marshals the client's request to the MTS component's stub, which, in turn, accesses the MTS component through its interface.

Figure 51.3   An MTS component in an out-of-process server

The MTS component can be installed in a remote server process on a separate computer as shown in Figure 51.4. Just as in any other remote server process, the client and remote server communicate across machines using DCOM.

Figure 51.4   An MTS component in a remote server process

Connection information is maintained in the MTS proxy. The connection between the MTS client and proxy remains open as long as the client requires a connection to the server, so it appears to the client that it has continued access to the server. In reality though, the MTS stub may deactivate and reactivate the object, conserving resources so that other clients may use the connection. For details on activating and deactivating, see "Managing resources with just-in-time activation and resource pooling".

Requirements for an MTS component

In addition to the COM requirements, MTS requires that the component be a dynamic-link library (DLL). Components that are implemented as executable files (.EXE files) cannot execute in the MTS runtime environment.

In addition, an MTS component must meet the following requirements:

Managing resources with just-in-time activation and resource pooling

MTS manages resources by providing

Just-in-time activation

The ability for an object to be deactivated and reactivated while clients hold references to it is called just-in-time activation. From the client's perspective, only a single instance of the object exists from the time the client creates it to the time it is finally released. Actually, it is possible that the object has been deactivated and reactivated many times. By having objects deactivated, clients can hold references to the object for an extended time without affecting system resources. When an object becomes deactivated, MTS releases all the object's resources, for example, its database connection.

When a COM object is created as part of the MTS environment, a corresponding context object is also created. This context object exists for the entire lifetime of its MTS object, across one or more reactivation cycles. MTS uses the object context to keep track of the object during deactivation. This context object, accessed by the IObjectContext interface, coordinates transactions. A COM object is created in a deactivated state and becomes active upon receiving a client request.

An MTS object is deactivated when any of the following occurs:

Resource pooling

Since MTS frees up idle system resources during a deactivation, the freed resources are available to other server objects. That is, a database connection that is no longer used by a server object can be reused by another client. This is called resource pooling.

Opening and closing connections to a database can be time-consuming. MTS uses resource dispensers to provide a way to reuse existing database connections rather than create new ones. A resource dispenser caches resources such as connections to a database, so that components within a package can share resources. For example, if you have a database lookup and a database update component running in a customer maintenance application, you would package those components together so that they can share database connections.

In the Delphi environment, the resource dispenser is the Borland Database Engine (BDE).

When developing MTS applications, you are responsible for releasing resources.

Releasing resources

You are responsible for releasing resources of an object. Typically, you do this by calling SetComplete and SetAbort methods after servicing each client request. These methods release the resources allocated by the MTS resource dispenser.

At this same time, you must release references to all other resources, including references to other objects (including MTS objects and context objects) and memory held by any instances of the component, such as using free in ObjectPascal.

The only time you would not include these calls is if you want to maintain state between client calls. For details, see "Stateful and stateless objects".

Object pooling

Just as MTS is designed to pool resources, it is also designed to pool objects. After MTS calls the deactivate method it calls the CanBePooled method, which indicates that the object can be pooled for reuse. If CanBePooled is set to TRUE, rather than destroying the object upon deactivation, MTS moves the object to the object pool. Objects within the object pool are available for immediate use to any other client requesting this object. Only when the object pool is empty does MTS create a new object instance.

Objects that return FALSE or that do not support the IObjectControl interface are destroyed.

Note: Object pooling and recycling is not available in this version of MTS. MTS calls CanBePooled as described, but no pooling takes place. This is provided for forward-compatibility to allow developers to use CanBePooled in their applications now so these applications can handle pooling when it becomes available. Currently, Delphi initializes CanBePooled to FALSE since object pooling is not yet available in MTS.

Accessing the object context

As with any COM object, a COM object using MTS must be created before it is used. COM clients create an object by calling the COM library function, CoCreateInstance.

Each COM object running in the MTS environment, must have a corresponding context object. This context object is implemented automatically by MTS and is used to manage the MTS component and coordinate transactions. The context object's interface is IObjectContext. To access most methods of the object context, you can use the ObjectContext property of the TMtsAutoObject object. For example, you can use the ObjectContext property as follows:

if ObjectContext.IsCallerInRole ('Manager') ...

Another way to access the Object context is to use methods in the TMtsAutoObject object:

if IsCallerInRole ('Manager') ...

You can use either of the above methods. However, there is a slight advantage of using the TMtsAutoObject methods rather than referencing the ObjectContext property when you are testing your application. For a discussion of the differences, see "Debugging and testing MTS objects".

MTS transaction support

The transaction support provided by MTS allows you to group actions into transactions. For example, in a medical records application, if you had a Transfer component to transfer records from one physician to another, you could have your Add and Delete methods in the same transaction. That way, either the entire Transfer works or it can be rolled back to its previous state. Transactions simplify error recovery for applications that must access multiple databases.

MTS transactions ensure that

When you declare that an MTS component is part of a transaction, MTS associates transactions with the component's objects. When an object's method is executed, the services that resource managers and resource dispensers perform on its behalf execute under a transaction. Work from multiple objects can be composed into a single transaction.

Transaction attributes

Every MTS component has a transaction attribute that is recorded in the MTS catalog. The MTS catalog maintains configuration information for components, packages, and roles. You administer the catalog using the MTS Explorer as described in "Administering MTS objects with the MTS Explorer".

Each transaction attribute can be set to these settings:

Requires a transaction

MTS objects must execute within the scope of a transaction. When a new object is created, its object context inherits the transaction from the context of the client. If the client does not have a transaction context, MTS automatically creates a new transaction context for the object.

Requires a new transaction

MTS objects must execute within their own transactions. When a new object is created, MTS automatically creates a new transaction for the object, regardless of whether its client has a transaction. An object never runs inside the scope of its client's transaction. Instead, the system always creates independent transactions for the new objects.

Supports transactions

MTS objects can execute within the scope of their client's transactions. When a new object is created, its object context inherits the transaction from the context of the client. This enables multiple objects to be composed in a single transaction. If the client does not have a transaction, the new context is also created without one.

Does not support transactions

MTS objects do not run within the scope of transactions. When a new object is created, its object context is created without a transaction, regardless of whether the client has a transaction. Use this for COM objects designed prior to MTS support.

Object context holds transaction attribute

An object's associated context object indicates whether the object is executing within a transaction and, if so, the identity of the transaction.

Resource dispensers can use the context object to provide transaction-based services to the MTS object. For example, when an object executing within a transaction allocates a database connection by using the BDE resource dispenser, the connection is automatically enlisted on the transaction. All database updates using this connection become part of the transaction, and are either committed or aborted. For more information, see Enlisting Resources in Transactions in the MTS documentation.

Stateful and stateless objects

Like any COM object, MTS objects can maintain internal state across multiple interactions with a client. Such an object is said to be stateful. MTS objects can also be stateless, which means the object does not hold any intermediate state while waiting for the next call from a client.

When a transaction is committed or aborted, all objects that are involved in the transaction are deactivated, causing them to lose any state they acquired during the course of the transaction. This helps ensure transaction isolation and database consistency; it also frees server resources for use in other transactions. Completing a transaction enables MTS to deactivate an object and reclaim its resources. See the following section for information on how to control when MTS releases your object state.

Maintaining state on an object requires the object to remain activated, holding potentially valuable resources such as database connections.

Note: Stateless objects are more efficient and, therefore, they are recommended.

Enabling multiple objects to support transactions

You use IObjectContext methods as shown in the following table to enable an MTS object to participate in determining how a transaction completes. These methods, together with the component's transaction attribute, allow you to enlist one or more objects into a single transaction.

Table 51.1   IObjectContext methods for transaction support

Method

Description

SetComplete

Indicates that the object has successfully completed its work for the transaction. The object is deactivated upon return from the method that first entered the context. MTS reactivates the object on the next call that requires object execution.

SetAbort

Indicates that the object's work can never be committed. The object is deactivated upon return from the method that first entered the context. MTS reactivates the object on the next call that requires object execution.

EnableCommit

Indicates that the object's work is not necessarily done, but that its transactional updates can be committed in their current form. Use this to retain state across multiple calls from a client. When an object calls EnableCommit, it allows the transaction in which it is participating to be committed, but it maintains its internal state across calls from its clients until it calls SetComplete or SetAbort or until the transaction completes.

EnableCommit is the default state when an object is activated. This is why an object should always call SetComplete or SetAbort before returning from a method, unless you want the object to maintain its internal state for the next call from a client.

DisableCommit

Indicates that the object's work is inconsistent and that it cannot complete its work until it receives further method invocations from the client. Call this before returning control to the client to maintain state across multiple client calls.

This prevents the MTS runtime environment from deactivating the object and reclaiming its resources on return from a method call. Once an object has called DisableCommit, if a client attempts to commit the transaction before the object has called EnableCommit or SetComplete, the transaction will abort.

You may use this, for example, to change the default state when an object is activated.

MTS or client-controlled transactions

Transactions can either be controlled directly by the client, or automatically by the MTS runtime environment.

Clients can have direct control over transactions by using a transaction context object (using the ITransactionContext interface). However, MTS is designed to simplify client development by taking care of transaction management automatically.

MTS components can be declared so that their objects always execute within a transaction, regardless of how the objects are created. This way, objects do not need to include any logic to handle the special case where an object is created by a client not using transactions. This feature also reduces the burden on client applications. Clients do not need to initiate a transaction simply because the component that they are using requires it.

With MTS transactions, you can implement the business logic of your application in your server objects. The server objects can enforce the rules so the client does not need to know about the rules. For example, in a physicians' medical application, an X-ray technician client can add and view X-rays in any medical record. It does not need to know that the application does not allow the X-ray technician to add or view any other type of medical record. That logic is in other server objects within the application.

Advantage of transactions

Allowing a component to either live within its own transaction or be part of a larger group of components that belong to a single transaction is a major advantage of the MTS runtime environment. It allows a component to be used in various ways, so that application developers can reuse application code in different applications without rewriting the application logic. In fact, developers can determine how components are used in transactions when packaging the component. They can change the transaction behavior simply by adding a component to a different package. For details about packaging components, see "Installing MTS objects into an MTS package".

Transaction timeout

The transaction timeout sets how long (in seconds) a transaction can remain active. Transactions that are still alive after the timeout are automatically aborted by the system. By default, the timeout value is 60 seconds. You can disable transaction timeouts by specifying a value of 0, which is useful when debugging MTS objects.

To set the timeout value on your computer,

  1. In the MTS Explorer, select Computer, My Computer.

    By default, My Computer corresponds to the local computer on which MTS is installed.

  2. Right-click and choose Properties and then choose the Options tab.

    The Options tab is used to set the computer's transaction timeout property.

  3. Change the timeout value to 0 to disable transaction timeouts.
  4. Click OK to save the setting and return to the MTS Explorer.

For more information on debugging MTS applications, see "Debugging and testing MTS objects".

Role-based security

MTS currently provides role-based security where you assign a role to a logical group of users. For example, a medical information application might define roles for Physician, X-ray technician, and Patient.

You define authorization for each component and component interface by assigning roles. For example, in the physicians' medical application, only the Physician may be authorized to view all medical records; the X-ray Technician may view only X-rays; and Patients may view only their own medical record.

Typically, you define roles during application development and assign roles for each package of components. These roles are then assigned to specific users when the application is deployed. Administrators can configure the roles using the MTS Explorer.

You can also set roles programmatically using the ObjectContext property TMtsAutoObject. For example,

if ObjectContext.IsCallerInRole ('Manager') ...

Another way to access the object context is to use methods of the MTS TMtsAutoObject object:

if IsCallerInRole ('Manager') ...

Note: For applications that require stronger security, context objects implement the ISecurityProperty interface, whose methods allow retrieval of the Window's security identifier (SID) for the direct caller and creator of the object, as well as the SID for the clients which are using the object.

Resource dispensers

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

BDE resource dispenser

The BDE resource dispenser manages pools of database connections for MTS components that use the standard database interfaces, allocating connections to objects quickly and efficiently. For remote MTS data modules, connections are automatically enlisted on an object's transactions, and the resource dispenser can automatically reclaim and reuse connections.

Shared property manager

The Shared Property Manager is a resource dispenser that you can use to share state among multiple objects within a server process. For example, you can use it to maintain the shared state for a multiuser game.

By using the Shared Property Manager, you avoid having to add a lot of code to your application; MTS provides the support for you. That is, the Shared Property Manager protects object state by implementing locks and semaphores to protect shared properties from simultaneous access. The Shared Property Manager eliminates name collisions by providing shared property groups, which establish unique name spaces for the shared properties they contain.

To use the Shared Property Manager resource, you first use the CreateSharedPropertyGroup helper function to create a shared property group. Then you can write all the properties to that group and read all the properties from that group. By using a shared property group, the state information is saved across all deactivations of an MTS object. In addition, state information can be shared among all MTS objects installed in the same package. You can install MTS components into a package as described in "Installing MTS objects into an MTS package".

The following example shows how to add code to support the Shared Property Manager in an MTS object. After the example are tips for you to consider when designing your MTS application for sharing properties.

Example: Sharing properties among MTS object instances

The following example creates a property group called MyGroup to contain the properties to be shared among objects and object instances. In this example, we have a Counter property that is shared. It uses the CreateSharedPropertyGroup helper function to create the property group manager and property group, and then uses the CreateProperty method of the Group object to create a property called Counter.

To get the value of a property, you use the PropertyByName method of the Group object as shown below. You can also use the PropertyByPosition method.

unit Unit1;
interface
uses
  MtsObj, Mtx, ComObj, Project2_TLB;
type
  Tfoobar = class(TMtsAutoObject, Ifoobar)
  private
    Group: ISharedPropertyGroup;
  protected
    procedure OnActivate; override;
    procedure OnDeactivate; override;
    procedure IncCounter;
  end;
implementation
uses ComServ;
{ Tfoobar }
procedure Tfoobar.OnActivate;
var
  Exists: WordBool;
  Counter: ISharedProperty;
begin
  Group := CreateSharedPropertyGroup('MyGroup');
  Counter := Group.CreateProperty('Counter', Exists);
end;
procedure Tfoobar.IncCounter;
var
  Counter: ISharedProperty;
begin
  Counter := Group.PropertyByName['Counter'];
  Counter.Value := Counter.Value + 1;
end;
procedure Tfoobar.OnDeactivate;
begin
  Group := nil;
end;
initialization
  TAutoObjectFactory.Create(ComServer, Tfoobar, Class_foobar, ciMultiInstance, tmApartment);
end.

Tips for using the Shared Property Manager

For objects to share state, they all must be running in the same server process.

You can only use shared properties to share between objects running in the same process. If you want instances of different components to share properties, you must install the components in the same MTS package. Because there is a risk that administrators will move components from one package to another, it's safest to limit the use of a shared property group to instances of components that are defined in the same DLL.

Components sharing properties must have the same activation attribute. If two components in the same package have different activation attributes, they generally won't be able to share properties. For example, if one component is configured to run in a client's process and the other is configured to run in a server process, their objects will usually run in different processes, even though they're in the same package.

Base clients and MTS components

It's important to understand the difference between clients and objects in the MTS runtime environment. Clients, or base clients in MTS terms, are not running under MTS. Base clients are the primary consumers of MTS objects. Typically, they provide the application's user interface or map the end-user's requests to the business functions defined in the MTS server objects. Alternatively, clients do not have the benefit of underlying MTS features. Clients do not get transaction support nor can they rely on resource dispensers.

The following table contrasts MTS components with base client applications.

Table 51.2   MTS server objects versus base clients

MTS components

Base clients

MTS components are contained in COM dynamic-link libraries (DLLs); MTS loads DLLs into processes on demand.

Base clients can be written as executable files (EXE) or dynamic-link libraries (DLL). MTS is not involved in their initiation or loading.

MTS manages server processes that host MTS components.

MTS does not manage base client processes.

MTS creates and manages the threads used by components.

MTS does not create or manage the threads used by base client applications.

Every MTS object has an associated context object. MTS automatically creates, manages, and releases context objects.

Base clients do not have implicit context objects. They can use transaction context objects, but they must explicitly create, manage, and release them.

MTS objects can use resource dispensers. Resource dispensers have access to the context object, allowing acquired resources to be automatically associated with the context.

Base clients cannot use resource dispensers.

MTS underlying technologies, COM and DCOM

MTS used the Component Object Model (COM) as its foundation to support the COM objects in client/server applications. COM defines a set of structured interfaces that enable components to communicate.

MTS uses DCOM for remote communication. To access a COM object on another machine, the client uses DCOM, which transparently transfers a local object request to the remote object running on a different machine. For remote procedure calls, DCOM uses the RPC protocol provided by Open Group's Distributed Computing Environment (DCE).

For distributed security, DCOM uses the NT Lan Manager (NTLM) security protocol. For directory services, DCOM uses the Domain Name System (DNS).

Resource pooling in the MTS environment is generally provided by an underlying database engine. In Delphi, resource pooling is provided by the Borland Database Engine. All database connections allocated by MTS objects come from this pool. These connections can participate in two-phase commit with MTS as the controller.

Overview of creating MTS objects

The process of creating an MTS component is as follows:

  1. Use the MTS Object wizard to create an MTS component.
  2. Add methods and properties to the application using the Type Library editor.For details on adding methods and properties using the Type Library editor, see "Working with type libraries."
  3. Debug and test the MTS component.
  4. Install the MTS component into a new or existing MTS package.
  5. Administer the MTS environment using the MTS Explorer.

Using the MTS Object wizard

Use the MTS Object wizard to create an MTS object that allows client applications to access your server within the MTS runtime environment. MTS provides extensive runtime support such as resource pooling, transaction processing, and role-based security.

To bring up the MTS Object wizard,

  1. Choose File|New.
  2. Select the tab labeled Multitier.
  3. Double-click the MTS Object icon.

In the wizard, specify the following:

ClassName

Specify the name the for the MTS class.

Threading Model

Choose the threading model to indicate how client applications can call your object's interface. This is the threading model that you commit to implementing in the MTS object. For more information on threading models, see"Choosing a threading model".

Note: The threading model you choose determines how the object is registered. You must make sure that your object implementation adheres to the model selected.

Transaction Model

Specify whether and how this MTS object supports transactions.

Generate event support code

Check this box to tell the wizard to implement a separate interface for managing events of your MTS object.

When you complete this procedure, a new unit is added to the current project that contains the definition for the MTS object. In addition, the wizard adds a type library project and opens the type library. Now you can expose the properties and methods of the interface through the type library. You expose the interface as you would expose any Automation object as described in "Exposing an application's properties, methods, and events"

The MTS object implements a dual interface, which supports both early (compile-time) binding through the vtable and late (runtime) binding through the IDispatch interface.

The MTS Object wizard implements the IObjectControl interface methods, Activate, Deactivate, and CanBePooled.

Choosing a threading model for an MTS object

The MTS runtime environment manages threads for you. MTS components should not create threads. Components must never terminate a thread that calls into a DLL.

When you specify the threading model when using the MTS wizard, you are specifying how the objects are assigned to threads for method execution.

Table 51.3   Threading models for COM objects

Threading model

Description

Implementation pros and cons

Single

No thread support. Client requests are serialized by the calling mechanism.

All objects of a single-threaded component execute on the main thread.

This is compatible with the default COM threading model, which is used for components that do not have a Threading Model Registry attribute or for COM components that are not reentrant. Method execution is serialized across all objects in the component and across all components in a process.

Allows components to use libraries that are not reentrant.

Very limited scalability.

Single-threaded, stateful components are prone to deadlocks. You can eliminate this problem by using stateless objects and calling SetComplete before returning from any method.

Apartment (or Single-threaded apartment)

Each object is assigned to a thread an apartment, which lasts for the life of the object; however, multiple threads can be used for multiple objects. This is a standard COM concurrency model. Each apartment is tied to a specific thread and has a Windows message pump.

Provides significant concurrency improvements over the single threading model.

Two objects can execute concurrently as long as they are in different activities. These objects may be in the same component or in different components.

Similar to a COM apartment, except that the objects can be distributed across multiple processes.

Note: These threading models are similar to those defined by COM objects. However, because the MTS environment provides more underlying support for threads, the meaning of each threading model differs here. Also, the free threading model does not apply to objects running in the MTS environment due to the MTS support for activities.

MTS activities

MTS supports concurrency through activities. Every MTS object belongs to one activity, which is recorded in the object's context. The association between an object and an activity cannot be changed. An activity includes the MTS object created by the base client, as well as any MTS objects created by that object and its descendants. These objects can be distributed across one or more processes, executing on one or more computers.

For example, a physician's medical application may have an MTS object to add updates and remove records to various medical databases, each represented by a different object. This add object may use other objects as well, such as a receipt object to record the transaction. This results in several MTS objects that are either directly or indirectly under the control of the base client. These objects all belong to the same activity.

MTS tracks the flow of execution through each activity, preventing inadvertent parallelism from corrupting the application state. This feature results in a single logical thread of execution throughout a potentially distributed collection of objects. By having one logical thread, applications are significantly easier to write.

When an MTS object is created from an existing context, using either a transaction context object or an MTS object context, the new object becomes a member of the same activity. In other words, the new context inherits the activity identifier of the context used to create it.

MTS allows only a single logical thread of execution within an activity. This is similar in behavior to a COM apartment, except that the objects can be distributed across multiple processes. When a base client calls into an activity, all other requests for work in the activity (such as from another client thread) are blocked until after the initial thread of execution returns back to the client.

For more information on threading in the MTS environment, search the MTS documentation for the topic, Components and Threading.

Setting the transaction attribute

You set a transaction attribute either at design time or at runtime.

At design time, the MTS Object wizard prompts you to choose the transaction attribute.

You can change the transaction attribute at runtime by using the Type Library editor.

To change a transaction attribute at runtime,

  1. Choose View|Type Library to open the Type Library editor.
  2. Select the class corresponding to the MTS object.
  3. Click the Transaction tab and choose the desired transaction attribute.

Note: If the MTS object is already installed into the runtime environment, you must first uninstall the object and reinstall it. Use Run|Install MTS objects to do so.

In addition, you can change the transaction attribute of an object installed in the MTS runtime environment by using the MTS Explorer.

Passing object references

You can pass object references, for example, for use as a callback, only in the following ways:

An object reference that is obtained in the above ways is called a safe reference. MTS ensures that methods invoked using safe references execute within the correct context.

Calls that use safe references always pass through the MTS runtime environment. This allows MTS to manage context switches and allows MTS objects to have lifetimes that are independent of client references.

Using the SafeRef method

An object can use the SafeRef function to obtain a reference to itself that is safe to pass outside its context.

The unit that defines the SafeRef function is Mtx.

SafeRef takes as input

SafeRef returns a pointer to the interface specified in the RIID parameter that is safe to pass outside the current object's context. It returns nil if the object is requesting a safe reference on an object other than itself, or the interface requested in the RIID parameter is not implemented.

When an MTS object wants to pass a self-reference to a client or another object (for example, for use as a callback), it should always call SafeRef first and then pass the reference returned by this call. An object should never pass a self pointer, or a self-reference obtained through an internal call to QueryInterface, to a client or to any other object. Once such a reference is passed outside the object's context, it is no longer a valid reference.

Calling SafeRef on a reference that is already safe returns the safe reference unchanged, except that the reference count on the interface is incremented.

When a client calls QueryInterface on a reference that is safe, MTS automatically ensures that the reference returned to the client is also a safe reference.

An object that obtains a safe reference must release the safe reference when it is finished with it.

For details on SafeRef see the SafeRef topic in the MTS documentation.

Callbacks

Objects can make callbacks to clients and to other MTS components. For example, you can have an object that creates another object. The creating object can pass a reference of itself to the created object; the created object can then use this reference to call the creating object.

If you choose to use callbacks, note the following restrictions:

Setting up a transaction object on the client side

A client base application can control transaction context through the ITransactionContextEx interface. The following code example shows how a client application uses CreateTransactionContextEx to create the transaction context. This method returns an interface to this object.

This example wraps the call to the transaction context in a call to OleCheck which is necessary because the CreateInstance method is not declared as safecall.

procedure TForm1.MoveMoneyClick(Sender: TObject);
begin
  Transfer(CLASS_AccountA, CLASS_AccountB, 100);
end;
procedure TForm1.Transfer(DebitAccountId, CreditAccountId: TGuid; Amount: Currency);
var
  TransactionContextEx: ITransactionContextEx;
  CreditAccountIntf, DebitAccountIntf: IAccount;
begin
  TransactionContextEx := CreateTransactionContextEx;
  try
    OleCheck(TransactionContextEx.CreateInstance(DebitAccountId,
      IAccount, DebitAccountIntf));
    OleCheck(TransactionContextEx.CreateInstance(CreditAccountId,
      IAccount, CreditAccountIntf));
    DebitAccountIntf.Debit(Amount);
    CreditAccountIntf.Credit(Amount);
  except
    TransactionContextEx.Abort;
    raise;
  end;
  TransactionContextEx.Commit;
end;

Setting up a transaction object on the server side

To control transaction context from the MTS server side, you create an instance of ObjectContext. In the following example, the Transfer method is in the MTS object. In using ObjectContext this way, the instance of the object we are creating will inherit all the transaction attributes of the object who is creating it. We wrap the call in a call to OleCheck because the CreateInstance method is not declared as safecall.

procedure TAccountTransfer.Transfer(DebitAccountId, CreditAccountId: TGuid;
 Amount: Currency);
var
  CreditAccountIntf, DebitAccountIntf: IAccount;
begin
  try
    OleCheck(ObjectContext.CreateInstance(DebitAccountId,
      IAccount, DebitAccountIntf));
    OleCheck(ObjectContext.CreateInstance(CreditAccountId,
      IAccount, CreditAccountIntf));
    DebitAccountIntf.Debit(Amount);
    CreditAccountIntf.Credit(Amount);
  except
    DisableCommit;
    raise;
  end;
  EnableCommit;
end;

Debugging and testing MTS objects

You can debug local and remote MTS objects. When debugging MTS objects, you may want to turn off transaction timeouts.

The transaction timeout sets how long (in seconds) a transaction can remain active. Transactions that are still alive after the timeout are automatically aborted by the system. By default, the timeout value is 60 seconds. You can disable transaction timeouts by specifying a value of 0, which is useful when debugging MTS objects.

For information on remote debugging, see the Remote Debugging topic in Online help.

When testing the MTS object, you may first want to test your object outside the MTS environment to simplify your test environment.

While developing an MTS server, you cannot rebuild a server when it is still in memory. You may get a compiler error like, "Cannot write to DLL while executable is loaded." To avoid this, you can set the package properties in the MTS Explorer to shut down the server when it is idle.

To shut down the MTS server when idle,

  1. In the MTS Explorer, right-click the package in which your MTS component is installed and choose Properties.
  2. Select the Advanced tab.

    The Advanced tab determines whether the server process associated with a package always runs, or whether it shuts down after a certain period of time.

  3. Change the timeout value to 0, which shuts down the server as soon as no longer has a client to service.
  4. Click OK to save the setting and return to the MTS Explorer.

Note: When testing outside the MTS environment, you do not reference the ObjectProperty of TMtsObject directly. The TMtsObject implements methods such as SetComplete and SetAbort that are safe to call when the object context is nil.

Installing MTS objects into an MTS package

MTS applications consist of a group of in-process MTS objects (or MTS remote data modules) running in a single instance of the MTS executive (EXE). A group of COM objects that all run in the same process is called a package. A single machine can be running several different packages, where each package is running within a separate MTS EXE.

You can group your application components into a single package to run within a single process. You might want to distribute your components into different packages to partition your application across multiple processes or machines.

To install MTS objects into a package,

  1. Choose Run|Install MTS Objects to install MTS objects into a package.
  2. Check the MTS objects to be installed.
  3. In the Install Object dialog box, choose the Into New Package to create a new package in which to install the MTS object or choose Into Existing Package to install the object into one of the existing MTS packages listed.
  4. Choose OK to refresh the MTS catalog, which makes the objects available at runtime.

Packages can contain components from multiple DLLs, and components from a single DLL can be installed into different packages. However, a single component cannot be distributed among multiple packages.

Administering MTS objects with the MTS Explorer

Once you have installed MTS objects into an MTS runtime environment, you can administer these runtime objects using the MTS Explorer. The MTS Explorer is a graphical user interface for managing and deploying MTS components. With the MTS Explorer, you can

For details on the MTS Explorer, see the MTS Administrator's Guide.

Using MTS documentation

The documentation accompanying Microsoft's MTS provides thorough details of MTS concepts, programming scenarios, and administration tools. This documentation is likely to help those who are new to developing MTS applications.

Here is the roadmap of MTS documentation that accompanies the Microsoft product.

Table 51.4   Microsoft MTS documentation roadmap

Source

Description

Setting Up MTS

Describes how to set up MTS and MTS components, including instructions for accessing Oracle databases from MTS application and installing MTS sample applications.

Getting Started with MTS

Provides an overview of the new features in MTS, gives a brief tour of the documentation, and contains a glossary of terms.

Quick Tour of MTS

Provides an overview of MTS.

MTS Administrator's Guide

 

Roadmap to the MTS Administrator's Guide

Describes the different ways to use the MTS Explorer to deploy and administer applications, and gives an overview of the MTS Explorer graphical interface.

Creating MTS Packages

Provides task-oriented documentation for creating and assembling MTS packages.

Distributing MTS Packages

Provides task-oriented documentation for distributing MTS packages.

Installing MTS Packages

Provides task-oriented documentation for installing and configuring MTS packages.

Maintaining MTS Packages

Provides task-oriented information for maintaining and monitoring MTS packages.

Managing MTS Transactions

Describes distributed transactions and the management of transactions using the MTS Explorer.

Automating MTS Administration

Provides a conceptual overview, procedures, and sample code explaining how to use the MTS scriptable objects to automate procedures in the MTS Explorer.

MTS Programmer's Guide

 

Overview and Concepts

Provides an overview of the product and how the product components work together, explains how MTS addresses the needs of client/server developers and system administrators, and provides in-depth coverage of programming concepts for MTS components.

Building Applications for MTS

Provides task-oriented information for developing ActiveXTM components for MTS.

MTS Administrative Reference

Provides a reference for using the MTS scriptable objects to automate procedures in the MTS Explorer.

MTS Reference

Provides a reference for the MTS application programming interface (API).