Integrating Java and Windows DNA

Microsoft® Windows® Distributed interNet Applications Architecture (Windows DNA) is the application development model for the Windows platform. Windows DNA is the first application architecture that integrates the Internet, client/server, and PC models of computing into a framework of distributed computing solutions. Windows DNA specifies how to:

  • Develop robust, scalable, distributed applications using the Windows platform.

  • Extend existing data and external applications to support the Internet.

  • Support a wide range of client devices maximizing the reach of an application.

Windows Distributed interNet Applications Architecture (DNA)

Because Windows DNA relies on a comprehensive and integrated set of services provided by the Windows platform, developers are free from the burden of building or assembling the required infrastructure for distributed applications and can focus on delivering business solutions.

Windows DNA Technologies

Windows DNA platform technologies and services address the requirements of all tiers of modern distributed applications–user interface and navigation, business process, and storage.

The heart of Windows DNA is the integration of Web and client/server application development models through the Component Object Model (COM). Windows DNA services are exposed in a unified way through COM for applications to use. These services include component management, Dynamic HTML, Web browser and server, scripting, transactions, message queuing, security, directory, database and data access, systems management, and user interface.

Windows DNA also fully embraces an open approach to Web computing, building on the many important standards efforts approved by bodies such as the World Wide Web Consortium and the Internet Engineering Task Force. Adherence to open protocols and published interfaces make it easy to integrate other vendor solutions and provides broad interoperability with existing systems.

Because Windows DNA is based on COM and open Internet standards, developers can use any language or tool to create compatible applications. COM provides a modern, language-independent object model that provides a standard way for applications to interoperate at all tiers of the architecture. Through COM, developers can extend any part of the application via pluggable software components that can be written in C++, Microsoft® Visual Basic®, Java, or other languages. Because of this open approach, Windows DNA supports a broad range of development tools today, including tools from Microsoft, Borland, Powersoft, and many other vendors.

Java/Windows DNA Integration Documents

The documents included here are reproduced by permission from the originating technology group at Microsoft. In many cases they are only a subgroup of the entire documentation for that technology. These documents were chosen because of their connection with Java.

Internet Information Server (IIS) is an Internet file and application server included with the Microsoft® Windows NT® Server operating system. IIS can be used alone as a Web server, or in conjunction with compatible technologies to set up Internet commerce, to access and manipulate data from a variety of data sources, and to build Web applications that take advantage of server script and component code to deliver client-server functionality. The Integrating Java with IIS documents show how IIS functionality is exposed to Java, concentrating on Active Server Pages (ASP). The section includes several white papers on ASP and reference information on the com.ms.iis.asp and com.ms.asp packages.

Microsoft Data Access Components (MDAC) enable Universal Data Access (UDA). UDA provides tool- and platform-independent access to information across the enterprise, including relational and non-relational. The Integrating Java with MDAC section focuses on the Microsoft® ActiveX® Data Objects (ADO) component of MDAC, and includes several white papers on ADO and reference information on the com.ms.wfc.data package.

Microsoft Transaction Server (MTS) is a component-based model and runtime environment that simplifies the task of developing application components by allowing you to perform work using transactions. The Integrating Java with MTS section includes several white papers, tutorials on building MTS applications and building MTS components in Java, and reference information on the com.ms.mtx package.

Microsoft Visual Studio is a complete suite of tools that meets both technical and business requirements for enterprise development. The Integrating Java with Visual Studio section focuses on Microsoft® Visual J++®. The documents provided include white papers on Microsoft® Visual Studio® extensibility and the use of Windows Foundation Classes for Java (WFC). Reference information on Visual J++ extensibility and the COM interfaces of the Visual J++ Code Model are also included.

DirectX Media Documents

In addition to describing Java's integration with Windows DNA, the integration documents include information on using Java with Microsoft® DirectX® media. DirectX media is a family of high-level APIs and multimedia controls that provide rich support for animation, streaming, and integration of the different media types. The Integrating Java with DirectX Media section provides reference information on Microsoft® DirectAnimation™, a DirectX media component that enables you to integrate different media types.

© 1999 Microsoft Corporation. All rights reserved. Terms of use.


Integrating Java with IIS

Microsoft® Internet Information Server (IIS) is an Internet file and application server included with the Microsoft® Windows NT® Server operating system. IIS can be used alone as a Web server, or in conjunction with compatible technologies to set up Internet commerce, to access and manipulate data from a variety of data sources, and to build Web applications that take advantage of server script and component code to deliver client-server functionality.

This section shows how IIS functionality is exposed to Java. This release concentrates on Active Server Pages (ASP). Exposure of the Internet Information Server Administration Objects (IISAO) to Java will be provided in future releases.

With ASP, you can activate your Web site using any combination of HTML, scripting, and components written in any language, including Java. When you make a change on the ASP file on the server, you need only save the changes to the file. The next time the Web page is loaded, the script will automatically be compiled.

The following IIS documents are provided.

White Papers

An ASP You Can Grasp: The ABCs of Active Server Pages introduces the ASP technology, the ASP object model, and the ASP built-in objects.

ASP Technology Feature Overview focuses on using ASP to create client/server Web applications.

COM Objects in ASP describes some of the basics of COM in relation to Microsoft® ActiveX™ Data Objects (ADO).

Managing In-Process COM Components Using MTS and ASP Technology presents a solution for managing in-process COM components using MTS, a sample Microsoft® Visual Basic® component, and ASP technology.

IIS 4.0 and MTS 2.0: Technology for the Web explains why MTS was integrated into the IIS architecture, describes the three major benefits of this integration, and shows how to develop server-side components that use MTS.

Package Reference

The com.ms.asp package contains the raw wrappers that use native Java types and support native Java interfaces. It's much easier to use the com.ms.iis.asp package.

The com.ms.iis.asp package creates new interfaces that expose the ASP object model more naturally.


com.ms.asp Package

Java Classes

This is preliminary documentation for IIS 5.0 and is subject to change.

When you install IIS version 4.0 or later, IIS creates a set of Java class files that define the ASP built-in objects. The classes use native Java types, and support native Java interfaces. You can refer to these classes directly, however, the Microsoft Windows Foundation Classes (MWFC) simplify creating Active Server Components. The MWFC creates new interfaces that expose the ASP object model. The goal is to retain all of the features and flavor of the ASP Programming model, but to expose it in a way that is more natural for the Java programmer.

For more information, see Implementing with Visual J++.

ASP provides the ObjectContext object that contains methods that return interfaces to the built-in objects. Your component can then use these object interfaces to access the methods and properties of the built-in objects. The reference pages for the Java version of the built-in objects list the method names. For details on the methods, see the C++ equivalent reference pages.

Interface | Description
IApplicationObject | Calls the methods and properties of the Application object.
IASPError | Returned by the IServer::GetLastError method. Describes an ASP error condition.
IObjectContext | Returns an interface to one of the built-in objects: IApplicationObject, IRequest, IResponse, IServer, or ISessionObject. For more information on the IObjectContext interface, see the COM section.
IReadCookie | Retrieves the values and attributes of a cookie stored in the read-only Cookies collection.
IRequest | Calls the methods and properties of the Request object.
IRequestDictionary | Indexes the subcollections of the IRequest interface. Also retrieves the individual values stored in the ClientCertificate collection.
IResponse | Calls the methods and properties of the Response object.
IScriptingContext (Obsolete) | Provided as support for legacy ASP applications that use the IScriptingContext to access the built-in objects.
IServer | Calls the methods and properties of the Server object.
ISessionObject | Calls the methods and properties of the Session object.
IStringList | Retrieves the values stored in a string list such as that in the QueryString, Form, or ServerVariables collections.
IVariantDictionary | Provides an index of the Application and Session collections.
IWriteCookie | Sets the values and attributes of a cookie stored in the write-only Cookies collection.

To use the built-in object interfaces in a Java component, you must import the ASP classes into your code as shown below.

import com.ms.asp.*;

For more information about the built-in objects, see the Built-in Objects Reference.


IApplicationObject Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IApplicationObject interface enables your component to use the methods and properties of the Application object.

Syntax

public interface asp.IApplicationObject
  extends com.ms.com.IUnknown
{
      //Methods
  public abstract asp.IVariantDictionary getStaticObjects();
  public abstract asp.IVariantDictionary getContents();
  public abstract void Lock();
  public abstract void UnLock();
  public abstract com.ms.com.Variant getValue(java.lang.String);
  public abstract void putValue(java.lang.String, com.ms.com.Variant);
}

See Also

IApplicationObject


IASPError Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IASPError interface exposes methods you can use to obtain information about an error condition.

Syntax

public interface asp.IASPError
  extends com.ms.com.IUnknown
{
..//Methods
  public abstract java.lang.String getASPCode();
  public abstract int getNumber();
  public abstract java.lang.String getSource();
  public abstract java.lang.String getFileName();
  public abstract int getLineNumber();
  public abstract java.lang.String getDescription();
  public abstract java.lang.String getASPDescription();
 }

See Also

ASPError Object


IReadCookie Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IReadCookie interface enables your component to retrieve the values stored in the read-only Cookies collection.

Syntax

public interface asp.IReadCookie
  extends com.ms.com.IUnknown
{
      //Methods
  public abstract com.ms.com.IUnknown get_NewEnum();
  public abstract Boolean getHasKeys();
  public abstract com.ms.com.Variant getItem(com.ms.com.Variant);
}

See Also

IReadCookie


IRequest Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IRequest interface enables your component to use the methods and properties of the Request object.

Syntax

public interface asp.IRequest
  extends com.ms.com.IUnknown
{
  public abstract asp.IRequestDictionary getBody();
  public abstract com.ms.com.Variant BinaryRead(com.ms.com.Variant);
  public abstract asp.IRequestDictionary getQueryString();
  public abstract asp.IRequestDictionary getCookies();
  public abstract asp.IRequestDictionary getServerVariables();
  public abstract int getTotalBytes();
  public abstract asp.IRequestDictionary getForm();
  public abstract asp.IRequestDictionary getClientCertificate();
  public abstract java.lang.Object getItem(java.lang.String);
}

See Also

IRequest


IRequestDictionary Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IRequestDictionary interface enables your component to retrieve values from the ClientCertificate collection.

Syntax

public interface asp.IRequestDictionary
  extends com.ms.com.IUnknown
{
  public abstract com.ms.com.IUnknown get_NewEnum();
  public abstract com.ms.com.Variant getItem(com.ms.com.Variant);
  public abstract com.ms.com.Variant getKey(com.ms.com.Variant);
  public abstract int getCount();
}

See Also

IRequestDictionary


IResponse Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IResponse interface enables your component to use the methods and properties of the Response object.

Syntax

public interface asp.IResponse
  extends com.ms.com.IUnknown
{
      //Methods
  public abstract void Add(java.lang.String, java.lang.String);
  public abstract com.ms.com.Variant getExpires();
  public abstract void BinaryWrite(com.ms.com.Variant);
  public abstract void putExpires(int);
  public abstract java.lang.String getCacheControl();
  public abstract void putCacheControl(java.lang.String);
  public abstract java.lang.String getContentType();
  public abstract void AppendToLog(java.lang.String);
  public abstract void BinaryWrite(com.ms.com.SafeArray);
  public abstract java.lang.String getCharSet();
  public abstract void putCharSet(java.lang.String);
  public abstract void Pics(java.lang.String);
  public abstract void WriteBlock(short);
  public abstract Boolean getBuffer();
  public abstract void putBuffer(Boolean);
  public abstract void Clear();
  public abstract asp.IRequestDictionary getCookies();
  public abstract void putExpiresAbsolute(double);
  public abstract void End();
  public abstract void AddHeader(java.lang.String, java.lang.String);
  public abstract void putContentType(java.lang.String);
  public abstract java.lang.String getStatus();
  public abstract void putStatus(java.lang.String);
  public abstract void Flush();
  public abstract void Redirect(java.lang.String);
  public abstract com.ms.com.Variant getExpiresAbsolute();
  public abstract void Write(com.ms.com.Variant);
}

See Also

IResponse


IScriptingContext Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IScriptingContext interface enables your component to retrieve interfaces to the built-in objects provided by ASP. You should not use this interface; use IObjectContext instead.

Syntax

public interface asp.IScriptingContext
  extends com.ms.com.IUnknown
{
      //Methods
  public abstract asp.IRequest getRequest();
  public abstract asp.IResponse getResponse();
  public abstract asp.IApplicationObject getApplication();
  public abstract asp.IServer getServer();
  public abstract asp.ISessionObject getSession();
}

Remarks

You can use the methods exposed by the IScriptingContext interface to access any of the ASP built-in objects. IScriptingContext, however, has been replaced by ObjectContext as the preferred way to access the built-in objects. You should use ObjectContext instead of IScriptingContext.

See Also

IScriptingContext


IServer Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IServer interface enables your component to use the methods and properties of the Server object.

Syntax

public interface asp.IServer
  extends com.ms.com.IUnknown
{
      //Methods
  public abstract java.lang.Object CreateObject(java.lang.String);
  public abstract java.lang.String MapPath(java.lang.String);
  public abstract java.lang.String HTMLEncode(java.lang.String);
  public abstract java.lang.String URLEncode(java.lang.String);
  public abstract void putScriptTimeout(int);
  public abstract int getScriptTimeout();
  public abstract void Execute (java.lang.String);
  public abstract void Transfer (java.lang.String);
  public abstract asp.IASPError getLastError();
}

See Also

IServer


ISessionObject Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The ISessionObject interface enables your component to use the methods and properties of the Session object.

Syntax

public interface asp.ISessionObject
  extends com.ms.com.IUnknown
{
      //Methods
  public abstract void Abandon();
  public abstract com.ms.com.Variant getValue(java.lang.String);
  public abstract void putValue(java.lang.String, com.ms.com.Variant);
  public abstract int getCodePage();
  public abstract void putCodePage(int);
  public abstract asp.IVariantDictionary getContents()
  public abstract int getLCID();
  public abstract asp.IVariantDictionary getStaticObjects();
  public abstract java.lang.String getSessionID();
  public abstract int getTimeout();
  public abstract void putTimeout(int);
  public abstract void putLCID(int);
}

See Also

ISessionObject


IStringList Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IStringList interface enables your component to retrieve values from the QueryString, Form, or ServerVariables collections.

Syntax

public interface asp.IStringList
  extends com.ms.com.IUnknown
{
  public abstract int getCount();
  public abstract com.ms.com.IUnknown get_NewEnum();
  public abstract com.ms.com.Variant getItem(com.ms.com.Variant);
}

See Also

IStringList


IVariantDictionary Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

You can use the IVariantDictionary interface to retrieve values from the Application Contents, Application StaticObjects, Session Contents and Session StaticObjects collections.

Syntax

public interface asp.IVariantDictionary extends com.ms.com.IUnknown
{

  public abstract com.ms.com.Variant getItem(com.ms.com.Variant);
  public abstract void putItem(com.ms.com.Variant, com.ms.com.Variant);
  public abstract com.ms.com.IUnknown get_NewEnum();
  public abstract com.ms.com.Variant getKey(com.ms.com.Variant);
  public abstract int getCount();
  public abstract void remove(com.ms.com.Variant);
  public abstract void removeAll();
}

See Also

IVariantDictionary


IWriteCookie Interface

This is preliminary documentation for IIS 5.0 and is subject to change.

The IWriteCookie interface enables your component to add new values and set the attributes of the write-only Cookie collection.

Syntax

public interface asp.IWriteCookie
  extends com.ms.com.IUnknown
{
      //Methods
  public abstract void putExpires(double);
  public abstract void putSecure(Boolean);
  public abstract void putPath(java.lang.String);
  public abstract com.ms.com.IUnknown get_NewEnum();
  public abstract Boolean getHasKeys();
  public abstract void putDomain(java.lang.String);
  public abstract void putItem(com.ms.com.Variant, java.lang.String);
}

See Also

IWriteCookie


com.ms.iis.asp Package

Package, com.ms.iis.asp

The com.ms.iis.asp package simplifies creating Active Server Components by creating new interfaces that expose the ASP object model. The goal is to retain all of the features and flavor of the ASP programming model, but to expose it in a way that is more natural for the Java programmer.

The class files that make up the Java Component Framework fall into two broad categories. The first group contains the classes that you will use to access the ASP built-in objects. You should use the AspContext class to access the other classes in this group. Example code demonstrating how to access each of the built-in objects is provided in the ASPContext class topics below.

The class files in the second group provide support to the various built-in objects. For example, the RequestDictionary class exposes methods you can use to manipulate the items in the Request collections.

Class | Description
Application Class | Provides access to objects and variables that have been assigned application scope
AspContext Class | Provides an easy way to access the built-in objects
Request Class | Retrieves the values the client passed to the server in an HTTP request
Response Class | Exposes methods for sending a response to the client browser
Server Class | Provides access to server utility methods
Session Class | Stores information and objects for use during a particular user session

Class | Description
Cookie Class | Encapsulates the attributes of a single cookie
CookieDictionary Class | Provides access to all of the cookies that are sent or received during an HTTP request
ObjectDictionary Class | The base class on which the application and session collections are based
RequestDictionary Class | Provides access to the various collections exposed by the request class


Application Class

The Application class provides access to objects and variables that have been assigned application scope. You can use the Application class to share information among users of an application. Because applications are designed to support multiple users, the Application class exposes the lock and unlock methods to prevent multiple user conflicts.

public Application(com.ms.asp.IapplicationObject)

Syntax

Application appvar = aspContext.GetApplication ();

Parts

appvar

A valid Application.

aspContext

A valid aspContext instance.

The Application class exposes the following methods and properties:

Method | Description
lock | Prevents other clients from accessing the variables stored in the Application object until the Unlock method is called
unlock | Releases variables that were locked by the Lock method.

Variable | Description
StaticObjects | Returns the ObjectDictionary that contains all of the objects added to the Application object with the <Object> tag.

Remarks

An example of getting an instance of the Application class is provided on the page describing the staticObjects property.

See Also

© 1999 Microsoft Corporation. All rights reserved. Terms of use.


Application.lock

The lock method prevents other clients from modifying properties of the Application object.

public void lock ()

Syntax

App.lock ();

See Also

unlock


Application.unlock

The unlock method removes the lock on the Application object so that properties can be modified by other clients.

public void unlock ()

Syntax

App.unlock ();

See Also

lock


Application.staticObjects

The StaticObjects property lists all of the items that have been added to the Application object with the <OBJECT> tag.

public ObjectDictionary staticObjects

Syntax

ObjectDictionary objvar = Applicationobject.staticObjects;

Parts

objvar

ObjectDictionary that receives the staticObjects property.

Applicationobject

An instance of the Application class.

See Also

Application Class


AspContext Class

The AspContext class provides access to the ASP built-in objects. This class simplifies obtaining a built-in object instance by exposing an easy-to-use syntax.

public com.ms.mtx.IObjectContext getObjectContext

The AspContext class exposes the following methods:

Method | Description
getApplication | Accesses the Application object
getObjectContext | Accesses the ObjectContext object
getRequest | Accesses the Request object
getResponse | Accesses the Response object
getServer | Accesses the Server object
getSession | Accesses the Session object

Remarks

The AspContext class uses the GetObjectContext method exposed by the IObjectContext class. IObjectContext is contained in the com.ms.mtx package. Because the AspContext encapsulates the GetObjectContext method, your Java file does not need to import the com.ms.mtx package, and can simply import the aspcomp package. However, if your component needs to access the methods exposed by the ObjectContext object, you will need to import the com.ms.mtx package. For more details on objectcontext see the Microsoft® Transaction Server documentation in the Platform SDK section of the MSDN™ Library.


AspContext.getApplication

The getApplication method creates an instance of the Application object.

public static Application getApplication ()

Syntax

Application Appvar = AspContext.getApplication ();

Parts

Appvar

A variable that will be used to refer to a particular instance of the Application object. The variable must be of type Application.

AspContext

An instance of the AspContext class.

Example

The following code accesses the staticObjects property of the Application object. If objects have been added to the Application object with the <OBJECT> tag, the checkcontinue function returns true, otherwise it returns false.

import com.ms.iis.asp.*;
import com.ms.mtx.*;

public class wfctestmain

{
   public boolean checkforstatic()
   {
      Application myapp = AspContext.getApplication();
      ObjectDictionary mydict = myapp.staticObjects;
      if (mydict.isEmpty())
      {
         //There isn’t anything in the StaticObjects
         return true;
      }
      else
    {
         //there's at least one object
         return false;
   }
  }
}


AspContext.getObjectContext

The getObjectContext method creates an instance of the ObjectContext object.

public static com.ms.mtx.IObjectContext getObjectContext ()

Syntax

IObjectContext Objcontextvar = AspContext.getObjectContext ();

Parts

Objcontextvar

A variable that will be used to refer to particular instance of the ObjectContext object. The variable is of type IObjectContext.

AspContext

An instance of the AspContext class.

Example

This example demonstrates creating an instance of the ObjectContext. The ObjectContext is checked to determine if the component was called by a transacted ASP script. If it was called, the method returns true; if it wasn’t, the method returns false.

import com.ms.iis.asp.*;
import com.ms.mtx.*;

public class testgetobjcont

{
  public Boolean istransacted()
  {
   IObjectContext objContext = AspContext.getObjectContext();
   MTx.GetObjectContext();
   if (objContext.IsInTransaction())
   {
  //The component was called in a transacted script
  return true;
   }
   else
   {
  //it wasn't
  return false;
   }

}


AspContext.getRequest

The getRequest method creates an instance of the Request object.

public static Request getRequest ()

Syntax

Request Requestvar = AspContext.getRequest ();

Parts

Requestvar

A variable that will be used to refer to a particular instance of the Request object. The variable must be of type Request.

AspContext

An instance of the AspContext class.

Example

The following example demonstrates getting a form from the Response object and returning the number of items on the form.

import com.ms.iis.asp.*;

public class getreqtest

{
   public int getReqcount()
   {
      Request reqMyReq = AspContext.getRequest();
      RequestDictionary reqdictVar = reqMyReq.getForm ();

      //Returns the number of items on the form
         return reqdictVar.getCount();

  }
}


AspContext.getResponse

The getResponse method creates an instance of the Response object.

public static Response getResponse ()

Syntax

Response Responsevar = AspContext.getRequest ();

Parameters

Responsevar

A variable that will be used to refer to a particular instance of the Response object. The variable must be of type Response.

AspContext

An instance of the AspContext class.

Example

The following example demonstrates using the getResponse method to send a response to the client. The example assumes that the component is being called after a form has been submitted with a field named "textin" that holds the string to be sent to the client.

import com.ms.iis.asp.*;

public class getresptest

{
   public boolean sendResp()
   {
      Response resMyRes = AspContext.getResponse();
         resMyRes.write(“Hello World”);
         return true
   }
}


AspContext.getServer

The getServer method creates an instance of the Server object.

public static Server getServer ()

Syntax

Server Servervar = AspContext.getServer ();

Parts

Servervar

A variable that will be used to refer to a particular instance of the Application object. The variable must be of type Server.

AspContext

An instance of the AspContext class.

Example

The following example demonstrates obtaining an instance of the Server object and calling its createobject method to create an instance of a COM component. This example uses the progid of the MSWC.AdRotator component.

import com.ms.iis.asp.*;

public class getservtest

{
   public boolean objcreate()
   {
      Server servMyServ = AspContext.getServer();
      //Returns OK if the object was created successfully
        Object objVar = servMyServ.createObject(“MSWC.AdRotator”);
      if (objVar != null)
      {
         return true;
      }
      else
      {
         return false;
      }

   }
}


AspContext.getSession

The getSession method creates an instance of the Session object.

public static Session getSession ()

Syntax

Session Sessionvar = AspContext.getSession ();

Parts

Sessionvar

A variable that will be used to refer to a particular instance of the Session object. The variable must be of type Session.

AspContext

An instance of the AspContext class.

Example

The following example demonstrates using the Session object to determine the current SessionID.

import com.ms.iis.asp.*;

public class getsesstest

{
   public String getSessId()
   {
      Session sessMySess = AspContext.getSession();
      //Returns the number of items on the form
      String strSessId = sessMySess.getSessionID();
      return strSessId;

  }
}


Cookie Class

The Cookie class represents a single member of the CookieDictionary collection that is exposed by both the Request and Response objects. The Cookie class exposes the same functionality as the IReadCookie and IWriteCookie interfaces which are contained in the com.ms.asp package.

public Cookies(com.ms.asp.IreadCookie)

The Cookie class exposes the following methods:

Method | Description
getHasKeys | Indicates if the cookie contains keys
getKeys | Returns an enumerator for accessing the cookie's keys
getItem | Returns the value of an item
getValue | Returns the value of the cookie
setDomain | Sets the domain attribute for a cookie
setExpires | Sets the expires attribute for a cookie
setItem | Sets the value for a particular cookie that has been implemented as a dictionary
setPath | Sets the path attribute for a cookie
setSecure | Sets the secure attribute for a cookie
setValue | Sets the value for a cookie that has not been implemented as a dictionary


Cookie.getHasKeys

The getHasKeys method returns a Boolean variable that indicates whether or not the Cookie class includes key values.

public boolean getHasKeys ()

Syntax

boolean cookievar = cookie.getHasKeys ();

Parts

cookievar

A boolean variable to hold the result of the function.

cookie

A CookieDictionary object.

Example

The following example defines a function that returns as true if a cookie called "cookiedata" contains key values. See the CustomerInfo sample in the Developer Samples section for a more complete example of using cookie values.

// testgetkeys
import aspcomp.*;
public class testgetkeys
{
   public boolean checkcookies()
   {
  Request myRequest = AspContext.getRequest();
  CookieDictionary myCookies = myRequest.getCookies();
  Cookie myCookie = myCookies.getCookie("cookiedata");
   if (myCookie.getHasKeys())
         {
         //The cookie has key values
   return true
    }
    else
    {
   //it doesn't
   return false
    }
  }
}

See Also

getKeys


Cookie.getItem

The getItem method returns a particular item from a Cookie object.

public String getItem(String itemName)

Syntax

String Itemvar = cookie.getItem(String itemname);

Parts

Itemvar

A String variable that will be used to refer to a particular field stored in the cookie.

Cookie

A valid cookie.

Itemname

The item to retrieve.

Example

The following example retrieves the Fname item from a cookie called cookCust. For a complete working program that uses this code, see the CustomerInfo Sample under Developer Samples.

String FName = cookCust.getItem(“Fname”);


Cookie.getKeys

The getKeys method returns an enumerator for a Cookie object, which you can use to access key values.

public wfc.util.IEnumerator getKeys ()

Syntax

Enumerator keylist = Cookie.getKeys ();

Parts

keylist

An Enumerator object that you can use to navigate through the cookie's key values.

Cookie

A valid cookie.

Example

The following Boolean function returns true if the cookie contains values, and false if it does not.

public Boolean checkgetkeys()
{
Request myRequest = AspContext.getRequest();
CookieDictionary myCookies = myRequest.getCookies();
Cookie myCookie = myCookies.getCookie("cookiedata");
Enumerator keylist = (myCookie.getKeys());
  if (keylist.hasMoreElements())
   {
    //The cookie has key values
    return true
   }
   else
    {
   //it doesn't
   return false
    }
}


Cookie.getValue

The getValue method returns a string that represents a Cookie object that has no keys.

public java.lang.String getValue ()

Syntax

String Cookievar = cookie.getValue ();

Parts

Cookievar

A string variable that will be used to refer to a particular field stored in the cookie.

Cookie

A valid cookie.

Example

The following example retrieves the value of a cookie.

import aspcomp.*;

public class getvalue
{
   public Boolean getcookie()
   {
      Request request = AspContext.getRequest();
      CookieDictionary cookDict = request.getCookies();
      Cookie myCook = cookDict.getCookie("CustInfo");
      String cookstring = myCook.getValue ();
      if (!cookstring .equals(""))
      {
         //there is a valid cookie
         return true
      }
      else
      {
         //there isn't
         return false
      }
   }
}


Cookie.setDomain

The setDomain method sets the domain value for a Cookie object.

public void setDomain (java.lang.String DomainName)

Syntax

Cookie.setDomain (String DomainName);

Parts

Cookie

A valid cookie.

DomainName

The domain name to set for the cookie.

Example

The following code sets the domain for a cookie to msn.com.

CookCust.setDomain(“msn.com”);


Cookie.setExpires

The setExpires method sets the expiration value for a Cookie object.

public void setExpires (wfc.app.Time d)

Syntax

Cookie.setExpires (Time Expiredate);

Parts

Cookie

A valid cookie.

Expiredate

A valid Time that represents the date and time for the cookie to expire.


Cookie.setItem

The setItem method sets the value for a Cookie object.

public void setItem (java.lang.String name, java.lang.String item)

Syntax

Cookie.setItem (String Name, String Item);

Parts

Cookie

A valid cookie.

Name

A string that specifies which item in the cookie is to be set.

Item

A string that specifies the value to set the item to.

Example

The following code snippet sets the value of an item called "Prefix" to the value that has been specified in a form. The CustomerInfo sample in Developer Samples uses this code within the context of a complete working program.

CookCust.setItem(“Prefix”, form.getString(“Prefix”));


Cookie.setPath

The setPath method sets the path value for a Cookie object.

public void setPath (java.lang.String path)

Syntax

Cookie.setPath (String Path);

Parts

Cookie

A valid cookie.

Path

A string that specifies the path for the cookie.

Example

The following code sets the path for the cookCust cookie to "/". This insures that the cookie is sent to all URLs for a given site.

CookCust.setPath("/");


Cookie.setSecure

The setSecure method sets the security value for a Cookie object.

public void setSecure (boolean secure)

Syntax

Cookie.setSecure (boolean secure);

Parts

Cookie

A valid cookie.

Secure

A boolean that specifies the path for the cookie.


Cookie.setValue

The setValue method sets the value for a Cookie object.

public void setValue (java.lang.String value)

Syntax

Cookie.setValue (String Value);

Parts

Cookie

A valid cookie.

Value

A string that specifies the new value for the cookie.

Example

The following code sets the value for myCookie to J. Jones, 1256.

myCookie.setValue (String "J. Jones, 1256")


CookieDictionary Class

The CookieDictionary class provides access to the cookies that have been added to either the Response or Request object.

public CookieDictionary (com.ms.asp.IrequestDictionary)

The CookieDictionary class exposes the following methods:

Methods | Description
clone | Creates a copy of the dictionary.
elements | Returns an IEnumerator object that contains the items in the dictionary.
get | Returns an item from the dictionary
getCookie | Retrieves a cookie from the dictionary.
getCount | Retrieves the number of cookies in the dictionary.
hasMoreElements | Returns a boolean indicating whether there are more items in the dictionary.
isEmpty | Returns a boolean indicating whether there are any items in the dictionary.
keys | Returns an IEnumerator object that contains the key values for the dictionary.
nextItem | Returns the next item stored in an enumerator.
put | Adds an item to the dictionary.
remove | This method is not supported.
reset | Returns to the first item in the dictionary.
size | Returns an int indicating the number of items in the dictionary.


CookieDictionary.clone

The clone method returns a CookieDictionary object that you can use to list the items.

public java.lang.Object clone ()

Syntax

Object objvar = Dictionary.clone ();

Parts

Objvar

An object variable that will receive a new VariantEnumerator.

Dictionary

A valid CookieDictionary.


CookieDictionary.elements

The elements method returns an IEnumerator interface that contains all of the items in the CookieDictionary.

public wfc.util.IEnumerator elements ()

Syntax

IEnumerator enumvar = Dictionary.elements ();

Parts

Enumvar

An enumeration variable that will contain the elements.

Dictionary

A valid CookieDictionary.


CookieDictionary.get

The get method returns an object from the CookieDictionary.

public java.lang.Object get (java.lang.Object key)

Syntax

Object objvar = Dictionary.get (Object key);

Parts

objvar

An object that holds the retrieved item.

Dictionary

A valid CookieDictionary.

key

The Object to get.


CookieDictionary.getCookie

The getCookie method returns a cookie from the CookieDictionary.

public wfc.iis.asp.Cookie getCookie (java.lang.String name)

Syntax

Cookievar = CookieDictionary.getCookie (name);

Parts

Cookievar

The variable that will represent the returned cookie. The variable must be of type Cookie.

CookieDictionary

A valid CookieDictionary.

Name

A string that represents the cookie to retrieve.


CookieDictionary.getCount

The getCount method returns an integer that is the number of items in the CookieDictionary.

public int getCount ()

Syntax

int Countvar = CookieDictionary.getCount ();

Parts

Countvar

An integer variable that will store the number of items in the CookieDictionary.

CookieDictionary

A valid CookieDictionary


CookieDictionary.hasMoreItems

The hasMoreItems returns a boolean indicating whether or not there are additional items in the CookieDictionary.

public boolean hasMoreItems ()

Syntax

boolean boolvar = CookieDictionary.hasMoreElements ();

Parts

boolvar

A boolean variable indicating whether there are any more items in the CookieDictionary.

CookieDictionary

A valid CookieDictionary.


CookieDictionary.isEmpty

The isEmpty method returns a boolean indicating whether or not there are any items in the CookieDictionary.

public boolean isEmpty ()

Syntax

boolean boolvar = CookieDictionary.isEmpty ();

Parts

boolvar

A boolean variable indicating whether there are any items in the CookieDictionary.

Dictionary

A valid CookieDictionary.


CookieDictionary.keys

The keys method returns an IEnumerator interface that can be used to list and access the objects in the CookieDictionary.

public wfc.util.IEnumerator keys ()

Syntax

IEnumerator enumvar = Cookiedictionary.keys ();

Parts

enumvar

An enumeration variable to hold the keys.

Cookiedictionary

A valid CookieDictionary.


CookieDictionary.nextItem

The nextItem method returns the next item from the CookieDictionary.

public java.lang.Object nextItem ()

Syntax

Object Objvar = Cookiedictionary.nextItem ();

Parts

Objvar

An Object variable to hold the item.

Cookiedictionary

A valid CookieDictionary.


CookieDictionary.put

The put method adds an item to a CookieDictionary or ObjectDictionary.

public java.lang.Object put (java.lang.Object key, java.lang.Object value)

Syntax

Object objvar = Dictionary.put (Object keyvar, Object valvar);

Parts

objvar

Previous object stored under the key.

Dictionary

A valid CookieDictionary.

keyvar

An object that identifies the item to add to the CookieDictionary.

valvar

The object to add.


CookieDictionary.remove

The remove method is exposed because it is inherited from the Dictionary class. It will always throw an AspComponentException when called. You should not use this method.

public java.lang.Object remove (java.lang.Object key)

Syntax

Object objvar = CookieDictionary.remove (Object key);

Parts

objvar

An Object variable that will hold the removed item.

Cookiedictionary

A valid CookieDictionary.

key

The object to remove.


CookieDictionary.reset

The reset method returns the CookieDictionary to the start of the collection.

public void reset ()

Syntax

CookieDictionary cookvar.reset ();

Parts

CookieDictionary

A valid CookieDictionary.


CookieDictionary.size

The size method returns an integer that specifies the number of items in the CookieDictionary, ObjectDictionary, or RequestDictionary.

public int size ()

Syntax

int intvar = Dictionary.size ();

Parts

Intvar

An integer variable that will indicate the size of the dictionary.

Dictionary

A valid CookieDictionary, ObjectDictionary, or RequestDictionary.


CookieEnumerator Class

The CookieEnumerator class exposes a single method for retrieving the next item from a CookieDictionary.

public CookieEnumerator(com.ms.com.IenumVariant, CookieDictionary)

The CookieEnumerator interface exposes this method:

Method | Description
nextItem | Returns an item from the CookieDictionary.


CookieEnumerator.nextItem

The nextItem method returns the subsequent element from the CookieEnumerator.

public java.lang.Object nextItem ()

Syntax

Object objectvar = CookieEnumerator.nextItem ();

Parts

objectvar

An Object variable that will hold the returned item.

CookieEnumerator

A valid CookieEnumerator object.


ObjectDictionary Class

The ObjectDictionary is the base class for the Session and Application collections, which you can use to store objects and variables. Both Session and Application also expose the staticObjects method that you can use to access objects that were added with the <OBJECT> tag. These objects are also returned through an ObjectDictionary.

The ObjectDictionary class exposes the following methods:

Method | Description
clone | Returns an Object that is a copy of the ObjectDictionary.
elements | Returns an IEnumerator object that contains the items in the dictionary.
get | Returns an item from the dictionary
getBoolean | Returns a Boolean variable.
getByte | Returns a byte variable.
getChar | Returns a char variable.
getCount | Retrieves the number of Objects in the dictionary.
getDouble | Returns a double variable.
getFloat | Returns a float variable.
getInt | Returns an integer variable.
getLong | Returns a long variable.
getObject | Returns an object variable.
getShort | Returns a short variable.
getString | Returns a string variable.
getType | Returns an integer variable indicating the type for a variable.
getVariant | Returns a variant variable.
hasMoreItems | Returns a boolean indicating whether there are more items in the dictionary.
isEmpty | Returns a boolean indicating whether there are any items in the dictionary.
keys | Returns an IEnumerator object that contains the key values for the dictionary.
nextItem | Returns the next item stored in an enumerator.
put | Adds an item to the dictionary.
remove | This method is not supported.
reset | Returns the ObjectDictionary to the first item.
setBoolean | Assigns a value to a Boolean variable.
setByte | Assigns a value to a byte variable.
setDate | Assigns a value to a date variable.
setDouble | Assigns a value to a double variable.
setFloat | Assigns a value to a float variable.
setInt | Assigns a value to an integer variable.
setObject | Assigns a value to an object variable.
setShort | Assigns a value to a short variable.
setString | Assigns a value to a string variable.
setVariant | Assigns a value to a variant variable.


ObjectDictionary.clone

The clone method returns an ObjectDictionary object that you can use to list the items.

public java.lang.Object clone ()

Syntax

Object objvar = Dictionary.clone ();

Parts

Objvar

An object variable that will receive a new VariantEnumerator.

Dictionary

A valid ObjectDictionary.


ObjectDictionary.elements

The elements method returns an IEnumerator interface that contains all of the items in the CookieDictionary.

public wfc.util.IEnumerator elements ()

Syntax

IEnumerator enumvar = Dictionary.elements ();

Parts

Enumvar

An enumeration variable that will contain the elements.

Dictionary

A valid ObjectDictionary.


ObjectDictionary.get

The get method returns an object from the ObjectDictionary.

public java.lang.Object get (java.lang.Object key)

Syntax

Object Objectvar = ObjectDictionary.get (key);

Parts

Objectvar

A variable that will represent the object. The variable must be of type object.

key

An object that specifies which object to retrieve.

ObjectDictionary

A valid ObjectDictionary


ObjectDictionary.getBoolean

The getBoolean method returns a variable of type Boolean.

public boolean getBoolean (java.lang.String p1)

boolean boolvar= Dictionary.getBoolean (String name)

Parameters

boolvar

A boolean variable.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the Boolean variable that is returned.


ObjectDictionary.getByte

The getByte method returns a variable of type byte.

public byte getByte (java.lang.String p1)

Syntax

byte bytevar = Dictionary.getByte (String name);

Parameters

bytevar

A variable to hold the returned byte.\

Dictionary

A valid ObjectDictionary

name

A string specifying the identifier of the byte variable that is returned.


ObjectDictionary.getChar

The getChar method returns a variable of type char.

public char getChar (java.lang.String p1)

Syntax

char charvar = Dictionary.getChar (String name);

Parameters

charvar

A variable to hold the returned char.

Dictionary

A valid ObjectDictionary.

name

A String specifying the identifier of the char variable that is returned.


ObjectDictionary.getCount

The getCount method returns an int indicating the number of items in the ObjectDictionary.

public int getCount ()

Syntax

int intvar = Dictionary.getCount();

intvar

An integer to hold the number of items in the ObjectDictionary.

Dictionary

A valid ObjectDictionary.


ObjectDictionary.getDouble

The getDouble method returns a variable of type double.

public double getDouble (java.lang.String p1)

Syntax

double doublevar = Dictionary.getDouble (String name);

Parameters

doublevar

A variable that will hold the returned double.

Dictionary

A valid ObjectDictionary.

name

A String specifying the identifier of the double variable that is returned.


ObjectDictionary.getFloat

The getFloat method returns a variable of type float.

public float getFloat (java.lang.String p1)

Syntax

float fltvar = Dictionary.getFloat (String name);

Parameters

fltvar

A variable to hold the returned float.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the float variable that is returned.


ObjectDictionary.getInt

The getInt method returns a variable of type int.

public int getInt (java.lang.String p1)

Syntax

int intvar = Dictionary.getInt (String name);

Parameters

intvar

A variable to hold the returned int.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the int variable that is returned.


ObjectDictionary.getLong

The getLong method returns a variable of type long.

public long getLong (java.lang.String p1)

Syntax

long lngvar = Dictionary.getLong (String name);

Parameters

lngvar

A variable to hold the returned long.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the long variable that is returned.


ObjectDictionary.getObject

The getObject method returns a variable of type object.

public java.lang.Object getObject (java.lang.String p1)

Syntax

Object objvar = Dictionary.getObject (String name);

Parameters

objvar

A variable to hold the returned Object.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the Object variable that is returned.


ObjectDictionary.getShort

The getShort method returns a variable of type short.

public short getShort (java.lang.String p1)

Syntax

short shrtvar = Dictionary.getShort (String name);

Parameters

shrtvar

A variable to hold the returned short.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the short variable that is returned.


ObjectDictionary.getString

The getString method returns a variable of type string.

public java.lang.String getString (java.lang.String p1)

Syntax

String strvar = Dictionary.getString (String name);

Parameters

strvar

A variable to hold the returned string.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the string variable that is returned.


ObjectDictionary.getType

The getType method returns a variable of type int. The int describes the type of variable that has been returned.

public int getType (java.lang.String p1)

Syntax

int intvar = Dictionary.getType (String name);

Parameters

intvar

A variable to hold the returned int describing the type.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the variable that is returned.


ObjectDictionary.getVariant

The getVariant method returns a variable of type VARIANT.

public com.ms.com.Variant getVariant (java.lang.String p1)

Syntax

Variant vntvar = Dictionary.getVariant (String name);

Parameters

vntvar

A variable to hold the returned Variant.

Dictionary

A valid ObjectDictionary.

name

A string specifying the identifier of the Object variable that is returned.


ObjectDictionary.isEmpty

The isEmpty method returns a Boolean variable that indicates whether or not the ObjectDictionary has any elements.

public Boolean isEmpty ()

Syntax

Boolean Boolvar= ObjectDictionary.isEmpty ();

Parts

Boolvar

A Boolean variable that will be either true if the dictionary has no elements or false if it has elements.

ObjectDictionary

A valid ObjectDictionary


ObjectDictionary.keys

The keys method returns an enumerator that you can use to list all of the VARIANT members of the ObjectDictionary, or to retrieve a particular VARIANT member.

public wfc.util.IEnumerator keys ()

Syntax

Enumeration Enumvar = ObjectDictionary.keys ();

Parts

Enumvar

A variable that will represent the enumerator. The variable must be of type VariantEnumerator.

ObjectDictionary

A valid ObjectDictionary


ObjectDictionary.put

The put method adds an object to the ObjectDictionary.

public java.lang.Object put (java.lang.Object key, java.lang.Object value)

Syntax

Object Objectvar = ObjectDictionary.put (Object key, Object value);

Parts

Objectvar

An object variable that refers to the object added to the dictionary.

ObjectDictionary

A valid ObjectDictionary.

key

An object that describes the object to be removed.

Value

The object to add to the dictionary.


ObjectDictionary.setBoolean

The setBoolean method creates a Boolean variable.

public void setBoolean (java.lang.String name, boolean b)

Parameters

name

A string that specifies the variable to set.

b

A Boolean variable.


ObjectDictionary.setByte

The setByte method creates a byte variable.

public void setByte (java.lang.String name, byte b)

Parameters

name

A string that specifies the variable to set.

b

A byte variable.


ObjectDictionary.setDouble

The setDouble method creates a double variable.

void setDouble (java.lang.String name, double d)

Parameters

name

A string that specifies the variable to set.

d

A double variable.


ObjectDictionary.setFloat

The setFloat method creates a float variable.

public void setFloat (String name, float f)

Parameters

name

A string that specifies the variable to set.

f

A float variable.


ObjectDictionary.setInt

The setInt method creates an integer variable.

public void setInt (java.lang.String name, int i)

Parameters

name

A string that specifies the variable to set.

i

An int variable.


ObjectDictionary.setObject

The setObject method creates an object variable.

public void setObject (java.lang.String name, java.lang.Object o)

Parameters

name

A string that specifies the variable to set.

o

An object variable.


ObjectDictionary.setShort

The setShort method creates a short variable.

public void setShort (java.lang.String name, short s)

Parameters

name

A string that specifies the variable to set.

s

A short variable.


ObjectDictionary.setString

The setString method creates a String variable.

public void setString (java.lang.String name, java.lang.String s)

Parameters

name

A string that specifies the variable to set.

s

A string variable.


ObjectDictionary.setVariant

The setVariant method creates a VARIANT variable.

public void setVariant (java.lang.String name, com.ms.com.Variant v)

Parameters

name

A string that specifies the variable to set.

v

A VARIANT variable.


ObjectDictionary.size

The size method returns a VARIANT that represents the number of objects in the ObjectDictionary.

public int size ()

Syntax

int Intvar = ObjectDictionary.size ();

Parts

Intvar

An integer variable that will represent the number of objects in the ObjectDictionary.

ObjectDictionary

A valid ObjectDictionary.


ObjectEnumerator Class

The ObjectEnumerator class exposes a single method for retrieving the next item from a ObjectDictionary.

public ObjectEnumerator(com.ms.com.IenumVariant, com.ms.iis.asp.IAspDictionary)

The ObjectEnumerator interface exposes this method:

Method | Description
nextItem | Returns an item from the ObjectDictionary.


ObjectEnumerator.nextItem

The nextItem method returns the subsequent element from the ObjectEnumerator.

public java.lang.Object nextItem ()

Syntax

Object objectvar = ObjectEnumerator.nextItem ();

Parts

objectvar

An Object variable that will hold the returned item.

ObjectEnumerator

A valid ObjectEnumerator object.


Request Class

The Request object retrieves the values that the client browser passed to the server during an HTTP request. The Request object encapsulates the Irequest interface.

The Request object extends InputStream, which provides access to the data sent from the client to the server as part of a POST request. You can access the parameters that were provided by the browser in the HTTP request, query string, and so forth, by retrieving one of the five Dictionary objects that the Request object owns with the getServerVariables, getClientCertificate, getForm, getCookies, and getQueryString methods.

public Request (com.ms.asp.IRequest com.ms.asp.IResponse)

The Request class provides the following methods:

Method | Description
getClientCertificate | Returns the ClientCertificates for the request.
getCookies | Returns the cookies for the request.
getForm | Returns the forms that have been sent with the request.
getQueryString | Returns the request’s query string.
getServerVariables | Returns the server variables associated with the request.
getTotalBytes | Returns the size of the request in bytes.
read | Returns the Request.


Request.getClientCertificate

The getClientCertificate method returns a RequestDictionary that represents the Request object’s ClientCertificate collection.

public com.ms.iis.asp.RequestDictionary getClientCertificate ()

Syntax

RequestDictionary RequestDictionaryvar = Request.getClientCertificate ();

Parts

RequestDictionaryvar

A RequestDictionary that represents the Request’s ClientCertificate collection.

Request

A valid Request object.


Request.getCookies

The getCookies method returns a RequestDictionary that represents the Request object’s Cookies collection.

public com.ms.iis.asp.CookieDictionary getCookies ()

Syntax

CookieDictionary CookieDictionaryvar = Request.getCookies ();

Parameters

CookieDictionaryvar

A CookieDictionary that represents a Request object’s Cookies collection.

Request

A valid Request object.


Request.getForm

The getForm method returns a RequestDictionary that represents the Request object’s Form collection.

public com.ms.iis.asp.RequestDictionary getForm ()

Syntax

RequestDictionary RequestDictionaryvar = Request.getForm ();

Parts

RequestDictionaryvar

A RequestDictionary that represents a Request object’s Form collection.

Request

A valid Request object.


Request.getQueryString

The getQueryString method returns a RequestDictionary that represents the Request object’s QueryString collection.

public com.ms.iis.asp.RequestDictionary getQueryString ()

Syntax

RequestDictionary RequestDictionaryvar = Request.getQueryString ();

Parts

RequestDictionaryvar

A RequestDictionary that represents a Request object’s Cookies collection.

Request

A valid Request object.


Request.getServerVariables

The getServerVariables method returns a RequestDictionary that represents the Request object’s ServerVariables collection.

public com.ms.iis.asp.RequestDictionary getServerVariables ()

Syntax

RequestDictionary RequestDictionaryvar = Request.getServerVariables ();

Parameters

RequestDictionaryvar

A RequestDictionary that represents a Request object’s ServerVariables collection.

Request

A valid Request object.


Request.getTotalBytes

The getTotalBytes method returns an integer that represents the number of bytes that was requested by the client.

public int getTotalBytes ()

Syntax

int Intvar = Request.getTotalBytes ();

Parts

Intvar

An integer that represents the number of bytes in the request.

Request

A valid Request object.


Request.read

The read method returns an integer.

public synchronized int read (byte[] b, int off, int len)

Syntax

int Intvar = Request.read (byte [] b, int off, int len);

Parts

Intvar

An integer variable.

Request

A valid Request object.

b

A byte array variable.

off

An integer variable indicating how far into the point in the byte array to start reading.

len

An integer variable indicating how many bytes to read from the byte array.

Return values

The method is called with a byte array, an offset point, and a length. It will return the byte array as specified. For further information, see either the Java.io.InputStream documentation in the Platform SDK, or read the internal comments in the Java.io.InputStream.java file in the \Java\Classes subdirectory.


RequestDictionary Class

The class RequestDictionary is used to encapsulate the various collections exposed by the Request object. Since these collections represent information sent from the client to the server, they are read-only.

The RequestDictionary class provides the following methods:

Method | Description
clone | Returns an Object that is a copy of the ObjectDictionary.
elements | Returns an IEnumerator object that contains the items in the dictionary.
get | Returns an item from the dictionary
getBoolean | Returns a Boolean variable.
getByte | Returns a byte variable.
getChar | Returns a char variable.
getCount | Retrieves the number of Objects in the dictionary.
getDate | Returns a long variable representing a date.
getDouble | Returns a double variable.
getFloat | Returns a float variable.
getInt | Returns an integer variable.
getLong | Returns a long variable.
getObject | Returns an object variable.
getShort | Returns a short variable.
getString | Returns a string variable.
getTime | Returns a wfc.app.Time variable.
getType | Returns an integer variable indicating the type for a variable.
getVariant | Returns a variant variable.
hasMoreItems | Returns a boolean indicating whether there are more items in the dictionary.
isEmpty | Returns a boolean indicating whether there are any items in the dictionary.
keys | Returns an IEnumerator object that contains the key values for the dictionary.
nextItem | Returns the next item stored in an enumerator.
put | Adds an item to the dictionary.
remove | This method is not supported.
reset | Returns the ObjectDictionary to the first item.
setBoolean | Assigns a value to a Boolean variable.
setByte | Assigns a value to a byte variable.
setDate | Assigns a value to a long variable representing a date.
setDouble | Assigns a value to a double variable.
setFloat | Assigns a value to a float variable.
setInt | Assigns a value to an integer variable.
setObject | Assigns a value to an object variable.
setShort | Assigns a value to a short variable.
setString | Assigns a value to a string variable.
setVariant | Assigns a value to a variant variable.


RequestDictionary.clone

The clone method returns an RequestDictionary object that you can use to list the items.

public java.lang.Object clone ()

Syntax

Object objvar = Dictionary.clone ();

Parts

Objvar

An object variable that will receive a new VariantEnumerator.

Dictionary

A valid RequestDictionary.


RequestDictionary.elements

The elements method returns an IEnumerator interface that contains all of the items in the RequestDictionary.

public wfc.util.IEnumerator elements ()

Syntax

IEnumerator enumvar = Dictionary.elements ();

Parts

Enumvar

An enumeration variable that will contain the elements.

Dictionary

A valid ObjectDictionary.


RequestDictionary.get

The get method returns an object from the RequestDictionary.

public java.lang.Object get (java.lang.Object key)

Syntax

Object Objectvar = Dictionary.get (key);

Parts

Objectvar

A variable that will represent the object. The variable must be of type object.

key

An object that specifies which object to retrieve.

Dictionary

A valid RequestDictionary


RequestDictionary.getBoolean

The getBoolean method returns a variable of type Boolean.

public boolean getBoolean (java.lang.String p1)

boolean boolvar= Dictionary.getBoolean (String name)

Parameters

boolvar

A boolean variable.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the Boolean variable that is returned.


RequestDictionary.getByte

The getByte method returns a variable of type byte.

public byte getByte (java.lang.String p1)

Syntax

byte bytevar = Dictionary.getByte (String name);

Parameters

bytevar

A variable to hold the returned byte.\

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the byte variable that is returned.


RequestDictionary.getChar

The getChar method returns a variable of type char.

public char getChar (java.lang.String p1)

Syntax

char charvar = Dictionary.getChar (String name);

Parameters

charvar

A variable to hold the returned char.

Dictionary

A valid RequestDictionary.

name

A String specifying the identifier of the char variable that is returned.


RequestDictionary.getCount

The getCount method returns an int indicating the number of items in the RequestDictionary.

public int getCount ()

Syntax

int intvar = Dictionary.getCount();

intvar

An integer to hold the number of items in the RequestDictionary.

Dictionary

A valid RequestDictionary.


RequestDictionary.getDate

The getDate method returns a variable of type date.

public long getDate (java.lang.String p1)

Syntax

long datevar = Dictionary.getDate (String name);

Parameters

datevar

A long variable that will hold the returned date.

Dictionary

A valid RequestDictionary.

name

A String specifying the identifier of the variable that is returned.


RequestDictionary.getDouble

The getDouble method returns a variable of type double.

public double getDouble (java.lang.String p1)

Syntax

double doublevar = Dictionary.getDouble (String name);

Parameters

doublevar

A variable that will hold the returned double.

Dictionary

A valid RequestDictionary.

name

A String specifying the identifier of the double variable that is returned.


RequestDictionary.getFloat

The getFloat method returns a variable of type float.

public float getFloat (java.lang.String p1)

Syntax

float fltvar = Dictionary.getFloat (String name);

Parameters

fltvar

A variable to hold the returned float.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the float variable that is returned.


RequestDictionary.getInt

The getInt method returns a variable of type int.

public int getInt (java.lang.String p1)

Syntax

int intvar = Dictionary.getInt (String name);

Parameters

intvar

A variable to hold the returned int.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the int variable that is returned.


RequestDictionary.getLong

The getLong method returns a variable of type long.

public long getLong (java.lang.String p1)

Syntax

long lngvar = Dictionary.getLong (String name);

Parameters

lngvar

A variable to hold the returned long.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the long variable that is returned.


RequestDictionary.getObject

The getObject method returns a variable of type object.

public java.lang.Object getObject (java.lang.String p1)

Syntax

Object objvar = Dictionary.getObject (String name);

Parameters

objvar

A variable to hold the returned Object.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the Object variable that is returned.


RequestDictionary.getShort

The getShort method returns a variable of type short.

public short getShort (java.lang.String p1)

Syntax

short shrtvar = Dictionary.getShort (String name);

Parameters

shrtvar

A variable to hold the returned short.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the short variable that is returned.


RequestDictionary.getString

The getString method returns a variable of type string.

public java.lang.String getString (java.lang.String p1)

Syntax

String strvar = Dictionary.getString (String name);

Parameters

strvar

A variable to hold the returned string.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the string variable that is returned.


RequestDictionary.getType

The getType method returns a variable of type int. The int describes the type of variable that has been returned.

public int getType (java.lang.String p1)

Syntax

int intvar = Dictionary.getType (String name);

Parameters

intvar

A variable to hold the returned int describing the type.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the variable that is returned.


RequestDictionary.getVariant

The getVariant method returns a variable of type VARIANT.

public com.ms.com.Variant getVariant (java.lang.String p1)

Syntax

Variant vntvar = Dictionary.getVariant (String name);

Parameters

vntvar

A variable to hold the returned Variant.

Dictionary

A valid RequestDictionary.

name

A string specifying the identifier of the Object variable that is returned.


RequestDictionary.isEmpty

The isEmpty method returns a Boolean variable that indicates whether or not the RequestDictionary has any elements.

public Boolean isEmpty ()

Syntax

Boolean Boolvar= ObjectDictionary.isEmpty ();

Parts

Boolvar

A Boolean variable that will be either true if the dictionary has no elements or false if it has elements.

ObjectDictionary

A valid RequestDictionary.


RequestDictionary.keys

The keys method returns an enumerator that you can use to list all of the VARIANT members of the RequestDictionary, or to retrieve a particular VARIANT member.

public wfc.util.IEnumerator keys ()

Syntax

Enumeration Enumvar = ObjectDictionary.keys ();

Parts

Enumvar

A variable that will represent the enumerator. The variable must be of type VariantEnumerator.

ObjectDictionary

A valid RequestDictionary.


RequestDictionary.put

The put method adds an object to the RequestDictionary.

public java.lang.Object put (java.lang.Object key, java.lang.Object value)

Syntax

Object Objectvar = ObjectDictionary.put (Object key, Object value);

Parts

Objectvar

An Object variable that refers to the object added to the dictionary.

ObjectDictionary

A valid RequestDictionary.

key

An Object that describes the object to be removed.

Value

The Object to add to the dictionary.


RequestDictionary.setBoolean

The setBoolean method creates a Boolean variable.

public void setBoolean (java.lang.String name, boolean b)

Syntax

Object Objvar = Dictionary.setBoolean (String key, boolean b);

Parameters

Objvar

A string that specifies the variable to set.

Dictionary

A valid RequestDictionary.

name

String describing the name of the boolean to add to the RequestDictionary.

b

A boolean variable.


RequestDictionary.setByte

The setByte method creates a byte variable.

public void setByte (java.lang.String name, byte b)

Parameters

name

A string that specifies the variable to set.

b

A byte variable.


RequestDictionary.setDate

The setDate method creates a date variable.

public void setDate (java.lang.String name, long d)

Parameters

name

A string that specifies the variable to set.

d

A long variable.


RequestDictionary.setDouble

The setDouble method creates a double variable.

void setDouble (java.lang.String name, double d)

Parameters

name

A string that specifies the variable to set.

d

A double variable.


RequestDictionary.setFloat

The setFloat method creates a float variable.

public void setFloat (String name, float f)

Parameters

name

A string that specifies the variable to set.

f

A float variable.


RequestDictionary.setInt

The setInt method creates an integer variable.

public void setInt (java.lang.String name, int i)

Parameters

name

A string that specifies the variable to set.

i

An int variable.


RequestDictionary.setObject

The setObject method creates an object variable.

public void setObject (java.lang.String name, java.lang.Object o)

Parameters

name

A string that specifies the variable to set.

o

An object variable.


RequestDictionary.setShort

The setShort method creates a short variable.

public void setShort (java.lang.String name, short s)

Parameters

name

A string that specifies the variable to set.

s

A short variable.


RequestDictionary.setString

The setString method creates a String variable.

public void setString (java.lang.String name, java.lang.String s)

Parameters

name

A string that specifies the variable to set.

s

A string variable.


RequestDictionary.setVariant

The setVariant method creates a VARIANT variable.

public void setVariant (java.lang.String name, com.ms.com.Variant v)

Parameters

name

A string that specifies the variable to set.

v

A VARIANT variable.


RequestDictionary.size

The size method returns a VARIANT that represents the number of objects in the RequestDictionary.

public int size ()

Syntax

int Intvar = ObjectDictionary.size ();

Parts

Intvar

An int variable that will represent the number of objects in the RequestDictionary.

Dictionary

A valid RequestDictionary.


Response Class

The Response class exposes methods that you can use to send output to the client.

public Response (com.ms.asp.IResponse)

The Response class provides the following methods:

Method | Description
addHeader | Adds a header to the Response object.
appendToLog | Adds a string to the end of the Web server log entry for this request.
clear | Removes any output that has been sent to a buffer.
end | Causes the Web server to stop processing the script and return the current result.
flush | Sends buffered output immediately.
getBuffered | Returns a Boolean indicating whether or not the response is being sent to a buffer.
getCacheControl | Returns the Cache Control header as a string. The Cache Control header is used to control whether proxy servers cache ASP output.
getCharSet | Returns a string describing the character set that will be used for the response.
getContentType | Returns a string that describes the content type of the response that is being sent to the client.
getCookies | Returns a CookieDictionary object that contains all of the cookies in the request object.
getExpires | Returns an integer that indicates the number of minutes before the response that has been cached on the server will expire.
getExpiresAbsolute | Returns a date that indicates when the response that has been cached on the server will expire.
getStatus | Returns a string that indicates the status of the server. This status is described using a status code. Status codes are defined in the HTTP specification.
isClientConnected | Returns a Boolean that indicates whether or not the client has disconnected.
pics | Sets the pics label to the response header. The pics setting should be a properly formatted pics label.
redirect | Causes the client browser to attempt to connect to another URL.
setBuffered | Determines whether or not the response will be sent to a buffer.
setCacheControl | Establishes the Cache Control header for the response. The Cache Control header determines whether or not proxy servers can cache the response to a buffer.
setCharSet | Establishes the character set to use for the response.
setContentType | Establishes the content type of the response.
setExpires | Establishes the number of minutes until the response will become invalid.
setExpiresAbsolute | Establishes the date when the response will become invalid.
setStatus | Establishes the status code for the response.
write | Sends output to the client.


addHeader

The addHeader method adds a header to the Response object.

public void addHeader (String name, String value)

Syntax

Response.addHeader (String name, String value);

Parts

name

A string indicating the header to add.

Value

The contents of the header.


appendToLog

The appendToLog method adds a string to the end of the Web server log entry for this request.

public void appendToLog (String s)

Syntax

Response.appendToLog (String s);

Parts

s

A string that will be added to the log.


clear

The clear method removes any output that has been sent to a buffer.

public void clear ()

Syntax

Response.clear ();


end

The end method causes the Web server to stop processing the script and return the current result.

public void end ()

Syntax

void Response.end ();


flush

The flush method writes any output that has been sent to a buffer. This method is inherited from java.io.OutputStream.

public void flush () throws IOException

Syntax

Response.flush ();


getBuffered

The getBuffered method returns a boolean indicating whether or not the response is being sent to a buffer.

public boolean getBuffered ()

Syntax

boolean boolvar = Response.getBuffered ();

Parts

boolvar

A boolean variable indicating whether or not the response is being sent to a buffer.


getCacheControl

The getCacheControl method returns the Cache Control header as a string. The Cache Control header is used by proxy servers to cache ASP output.

public String getCacheControl ()

Syntax

String stringvar = Response.getCacheControl ();

Parts

stringvar

A string variable that will hold the Cache Control header value.

Response

A valid Response object.


getCharSet

The getCharset method returns a string describing the character set that will be used for the response.

public String getCharSet ()

Syntax

String stringvar = Response.getCharSet ();

Parts

stringvar

A String variable that will hold the name of the character set.

Response

A valid Response object.


getContentType

The getContentType method returns a string that describes the content type of the response that is being sent to the client.

public String getContentType ()

Syntax

String stringvar = Response.getContentType ();

Parts

stringvar

A string variable that describes the content type of the response.

Response

A valid Response object.


getCookies

The getCookies method returns a CookieDictionary object that contains all of the cookies in the Response object.

public CookieDictionary getCookies ()

Syntax

CookieDictionary Dictvar = Response.getCookies ();

Parts

Dictvar

A CookieDictionary object to hold the cookies.

Response

A valid Response object.


getExpires

The getExpires method returns an integer that indicates the number of minutes that the response may be cached by the browser. If the client refreshes before this amount of time has elapsed, they will receive the response.

public int getExpires ()

Syntax

int intvar = Response.getExpires ();

Parts

intvar

An integer variable that will receive the number of minutes before the response will expire.

Response

A valid Response object.


getExpiresAbsolute

The getExpiresAbsolute method returns a Time that indicates when the response that has been cached by the browser will expire. If the client requests the response before this date they will receive the response.

public wfc.app.Time getExpiresAbsolute ()

Syntax

Time timevar = Response.getExpiresAbsolute ();

Parts

timevar

A Time variable that indicates when the cache response will expire.

Response

A valid Response object.


getStatus

The getStatus method returns a string that indicates the status of the server. This status is described using a status code. Status codes are defined in the HTTP specification.

public string getStatus ()

Syntax

String stringvar = Response.getStatus ();

Parts

stringvar

A string variable containing the response’s status code.

Response

A valid Response object.


isClientConnected

The isClientConnected method returns a boolean that indicates whether or not the client has disconnected.

public boolean isClientConnected ()

Syntax

boolean boolvar = Response.isClientConnected ();

Parts

boolvar

A boolean variable indicating whether or not the connection is still connected.

Response

A valid Response object.


pics

The pics method sets the pics label to the response header. The pics setting should be a properly formatted pics label.

public void pics (String str)

Syntax

Response.pics (String str) ;

Parts

str

A string variable containing the response’s pics setting.

Response

A valid Response object.


redirect

The redirect method causes the client browser to attempt to connect to another URL.

public void redirect (String str)

Syntax

Response.redirect (String str);

Parts

str

A string variable containing the new URL.

Response

A valid Response object.


setBuffered

The setBuffered method determines whether or not the response will be sent to a buffer.

public void setBuffered (boolean fIsBuffered)

Syntax

Response.setBuffered (boolean fIsBuffered);

Parts

flsBuffered

A boolean indicating whether or not the ASP output should be sent to a buffer.

Response

A valid Response object.


setCacheControl

The setCacheControl method establishes the Cache Control header for the response. The Cache Control header determines whether or not proxy servers can cache the response.

public void setCacheControl (String strCacheControl)

Syntax

Response.setCacheControl (String strContentType);

Parts

strContentType

A string indicating the type of content that is being sent in the response.

Response

A valid Response object.


setCharSet

The setCharSet method establishes the character set to use for the response.

public void setCharSet (String strCharSet)

Syntax

Response.setCharSet (String strCharSet);

Parts

strCharSet

A string specifying the character set to use for the response.

Response

A valid Response object.


setContentType

The setContentType method establishes the content type header for the response.

public void setContentType (String strContentType)

Parts

Response.setContentType(String strContentType);

Parts

Response

A valid Response object.

strContentType

A string specifying the type of content that will be sent in the response.


setExpires

The setExpires method establishes the number of minutes until the response will become invalid.

public void setExpires (int iExpirationMinutes)

Syntax

Response.setExpires (int iExpirationMinutes);

Parts

iExpirationMinutes

An integer specifying the amount of time before the response will expire.

Response

A valid Response object.


setExpiresAbsolute

The setExpiresAbsolute method establishes the date when the response will cease to be valid.

public void setExpiresAbsolute (wfc.app.Time dateExpire)

Syntax

Response.setExpiresAbsolute (wfc.app.Time dateExpire);

Parts

dateExpire

A Time specifying the exact date and time for the response to expire.

Response

A valid Response object.


setStatus

The setStatus method establishes the status code for the response.

public void setStatus (String strStatus)

Syntax

Response.setStatus (String strStatus);

Parts

strStatus

A string specifying the status of the response.

Response

A valid Response object.


write

The write method sends output to the client. This is an overloaded method, so the output will be determined by the input parameters. One version of the method is defined in aspcomp.Response, the other three versions are inherited from java.io.OutputStream interface.

public void write (String str)

public void write (byte [] b, int off, int len)

throws IOException, NullPointerException, IndexOutOfBoundsException

Syntax

Response.write (String str);

or

Response.write (byte [] b, int off, int len);

Parts

Response

A valid Response object.

str

A string specifying the output to send to the client.

b

A byte array to write to the output stream.

off

An integer specifying the point in the byte array to start writing from.

len

An integer specifying the number of bytes to write.


Server Class

You can use the methods exposed by the Server class to set properties for the server. The Server class also provides utility functions, such as creating object instances.

public Server (com.ms.asp.IServer)

The Server class provides the following methods:

Method | Description
CreateObject | Creates an instance of a COM object
GetScriptTimeout | Returns the timeout period for a script
HTMLEncode | Returns a string that represents the html equivalent of the string that is passed in
MapPath | Maps the specified relative or virtual path to the corresponding physical directory on the server
SetScriptTimeout | Establishes the amount of time that can pass before a script will become invalid
URLPathEncode | Applies URL encoding rules, including escape characters, to a path
URLEncode | Applies URL encoding rules, including escape characters, to a specified string


Server.createObject

The createObject method creates an instance of the COM component specified by the progID parameter.

public Object createObject (string progID)

Syntax

Object objvar = Server.createObject(String progID);

Parts

objvar

An Object variable that will be a reference for the new object instance.

Server

A valid Server object.

progID

A string specifying the Program Identifier for the object to create.

Remarks

You should always use the createObject method to create instances of COM objects that your class needs. Furthermore, if you are creating any other Java objects that are registered with Microsoft® Transaction Server (MTS), and which support transactions, you should create them via createObject, as well, and not via the new operator.


getScriptTimeout

The getScriptTimeout method returns an integer that represents the amount of time that can pass before scripts will become invalid.

public int getScriptTimeout ()

Syntax

int intvar = Server.getScriptTimeout ();

Parts

intvar

An integer variable that will contain the timeout variable after the method returns.

Server

A valid Server object.


HTMLEncode

The HTMLEncode method returns a string that represents the HTML equivalent of the string that is passed in.

public String HTMLEncode (String str)

Syntax

String stringvar = Server.HTMLEncode (String str);

Parts

stringvar

A String that will contain the HTML equivalent of the input string.

Server

A valid instance of a Server object.

str

A String containing the text to encode.


mapPath

The mapPath method maps the specified relative or virtual path to the corresponding physical directory on the server.

public String mapPath (String path)

Syntax

String strvar = Server.mapPath (String Path);

Parts

strvar

A String that will contain the physical path after the method returns.

Server

A valid instance of a Server object.

path

The path to map.


setScriptTimeout

The setScriptTimeout method establishes the amount of time that can pass before scripts will become invalid.

public void setScriptTimeout (int timeout)

Syntax

Server.setScriptTimeout (int timeout);

Parts

Server

A valid instance of a Server object.

timeout

An integer specifying when the amount of time before scripts timeout on the server.


URLEncode

The URLEncode method applies URL encoding rules, including escape characters, to a specified string.

public string URLEncode (String str)

Syntax

String strvar = Server.URLEncode (String str);

Parts

strvar

A String that will contain the encoded string after the method returns.

Server

A valid instance of a Server object.

str

A String containing the URL to encode.


URLPathEncode

The URLPathEncode method applies URL encoding rules, including escape characters, to a specified path.

public String URLEncode (String str)

Syntax

String strvar = Server.URLPathEncode (String str);

Parts

strvar

A String that will contain the encoded string after the method returns.

Server

A valid instance of a Server object.

str

A String containing the path to encode.


Session Class

You can use the Session object to store information needed for a particular user-session. Variables stored in the Session object are not discarded when the user jumps between pages in the application.

The Web server automatically creates a Session object when a Web page is requested by a user who does not already have a session. The server destroys the Session object when the session expires or is abandoned.

One common use for the Session object is to store user preferences set on a previous visit to the Web application, such as high, medium, or low graphics.

public Session (com.ms.asp.ISessionObject)

The Session class provides the following methods:

Method | Description
abandon | Destroys all objects that have been created with Session scope and releases their resources
getCodePage | Returns an integer indicating the code page that will be used to generate dynamic content
getLCID | Returns an integer indicating the location identifier that will be used to generate dynamic content during the session
getSessionID | Returns an int that is the identifier set for the session
getTimeout | Returns an int indicating the amount of time in minutes that can elapse before the session will become invalid
setCodePage | Establishes the code page that will be used to generate dynamic content
setLCID | Establishes the location identifier that will be used to generate dynamic content during the session
setTimeout | Establishes the period of time that can elapse before the session becomes invalid
staticObjects | Returns an ObjectDictionary that contains all of the objects that have been created with session scope


abandon

The abandon method destroys all objects that have been created with Session scope and releases their resources. If abandon is not called, these objects are destroyed when the session times out.

public void abandon ()

Syntax

Session.abandon ();

Parts

Session

A valid instance of a Session object.


getCodePage

The getCodePage method returns an integer indicating the code page that will be used to display dynamic content.

public int getCodePage ()

Syntax

int intvar = Session.getCodePage ();

Parts

intvar

An integer variable that will hold the code page description.

Session

A valid instance of a Session object.


getLCID

The getLCID method returns an integer indicating the location identifier that will be used to display dynamic content during the session.

public int getLCID ()

Syntax

int intvar = Session.getLCID ();

Parts

intvar

An integer variable that will hold the location identifier.

Session

A valid instance of a Session object.


getSessionID

The getSessionID method returns a string that is the identifier set for the session.

public String getSessionID ()

Syntax

String strvar = Session.getSessionID ();

Parts

strvar

A String variable that will hold the session identifier.

Session

A valid instance of a Session object.


getTimeout

The getTimeout method returns an integer indicating the amount of time in minutes that can elapse before the session will become invalid. If the client does not request a new page or refresh the session before the timeout period is reached, the session ends.

public int getTimeout ()

Syntax

int intvar = Session.getTimeout ();

Parts

intvar

An integer variable that will hold the code page description.

Session

A valid instance of a Session object.


setCodePage

The setCodePage method establishes the code page that will be used to display dynamic content.

public void setCodePage (int cp)

Syntax

Session.setCodePage (int cp);

Parts

Session

A valid instance of a Session object.

cp

An integer that establishes the code page.


setLCID

The setLCID method establishes the location identifier that will be used to display dynamic content during the session.

public void setLCID (int lcid)

Syntax

Session.setLCID (int lcid);

Parts

Session

A valid instance of a Session object.

cp

An integer that establishes the location identifier.


setTimeout

The setTimeout method establishes the period of time that can elapse before the session becomes invalid.

public void setTimeout (int timeout)

Syntax

Session.setTimeout (int timeout);

Parts

Session

A valid instance of a Session object.

timeout

An integer that establishes the timeout period for the session.


staticObjects

The staticObjects method returns an ObjectDictionary that contains all of the objects that have been created with session scope.

public ObjectDictionary staticObjects;

Syntax

ObjectDictionary objvar = Session.staticObjects;

Parts

objvar

ObjectDictionary that receives the staticObjects property.

Session

A valid Session object.


An ASP You Can Grasp: The ABCs of Active Server Pages

Nancy Winnick Cluts
Developer Technology Engineer
Microsoft Corporation

Updated: May 28, 1997

Contents
What Is an ASP File?
Is It Hard to Use?
Aw, Mom. Not Another Object Model!
Where to Find More Information on ASP

Active Server Pages is an open, compile-free application environment in which you can combine HTML, scripts, and reusable ActiveX server components to create dynamic and powerful Web-based business solutions. Active Server Pages enables server side scripting for IIS with native support for both VBScript and Jscript.

-- Microsoft Internet Information Server Web site (http://www.microsoft.com/iis/default.asp)

That's what they say, at least. When I read that paragraph, I want to know what this means to me as a developer, whether the Active Server Pages (ASP) technology is easy to use, and, most of all, how it works. So, I took a look at the documentation, attended a couple of presentations at Tech*Ed 97, surfed the Internet, and came up with some answers about what Active Server Pages really is, the technology's complexity level, and where you can find more detailed information (lots of links!).

What Is an ASP File?

Files created with Active Server Pages have the extension .ASP. This story is an example; take a look at the address above. With ASP files, you can activate your Web site using any combination of HTML, scripting—such as JavaScript or Microsoft® Visual Basic® Scripting Edition (VBScript)—and components written in any language. This means your ASP file is simply a file that can contain any combination of HTML, scripting, and calls to components. When you make a change on the ASP file on the server, you need only save the changes to the file—the next time the Web page is loaded, the script will automatically be compiled. How does this happen? It works because ASP technology is built directly into Microsoft Web servers, and is thus supported on all Microsoft Web servers: Microsoft® Windows NT® Internet Information Server (IIS) 3.0, Microsoft® Windows NT® Workstation, and Microsoft® Windows® 95 Personal Web Server.

A few examples of the use of Active Server Pages, as found in the white paper Using Active Server Pages with IIS 3.0 (http://www.microsoft.com/iis/Partners/aspwp.asp?A=8&B=2).

  • Put your employee handbook online, rather than printing copies that are soon obsolete. An added benefit is the reduction of administrative costs when employees can access and update their own records, such as address and health-plan benefits. I know of several administrative assistants who would love this one!

  • Tie your online store to your existing inventory database and order-processing system.

  • Give every visitor to your site a personalized view of just the information he or she seeks, and automatically flag what is new since the last visit.

I know what you're thinking: "So what? I can write CGI scripts right now that will let me update information and have it reloaded next time the page is loaded." That's true, but ASP runs as a service of the Web server and is optimized for multiple threads and multiple users. This means that it's fast, and it's easy to implement. If you use ASP, you can separate the design of your Web page from the nitty-gritty details of programming access to databases and applications. This frees up the programmer to do what she does best--code like crazy--and, conversely, frees the designer to worry about just the design rather than the database.

It all works together via scripting.

An example is a form that is used to pass a ticker symbol request in the URL to the ASP files. The first part of the ASP file calls a component that talks to a stock-price server. Properties of this object, such as opening and closing price, can then be easily inserted in the HTML. The programmer can work in any language, and need worry only about how to talk to the stock-price server. The HTML author need know only how to script the component, and does not care how the stock-price server works.

Is It Hard to Use?

I don't know about you, but whenever people start talking about doing things on the server, I start to get nervous. I think a lot about threading issues, synchronization, and generally stuff without a user interface. I assume it's going to be hard to do. Well, using ASP is about as easy as anything I've come across in years.

If you're an experienced programmer, you don't even have to learn a new language to create ASP files—you can use any language that supports ActiveX scripting. If you already develop with Microsoft® Visual Basic®, using VBScript is a snap!

Or, if you know how to author pages in HTML, you're probably ready to advance your skills a notch—and ASP is the perfect reason. Learning VBScript (http://www.microsoft.com/vbscript/) is not hard. Neither is Microsoft® JScript® (http://www.microsoft.com/jscript/), Microsoft's implementation of JavaScript.

You don't even have to write your own controls to start using Active Server Pages. You can use any off-the-shelf control that can be run on a server and has no user interface. The reason it should not have a user interface becomes obvious when you picture some hapless computer operator sitting in front of the server, dismissing dialog boxes meant for the user's machine. I mean, why should the operator care whether you've logged on successfully? User interfaces are for client applications (applications that the user is running)—not for server-side scripting.

Aw, Mom. Not Another Object Model!

I really hate doing this to you, but I'm going to have to use that overused and overly complex term "object model" again. Here's how it works. When a browser requests an ASP file from your Web server, your Web server calls Active Server Pages to read through the ASP file, executing any of the commands contained within and sending the resulting HTML page to the browser. An ASP file can contain any combination of HTML, script, or commands. The script can assign values to variables, request information from the server, or combine any set of commands into procedures.

ASP uses the delimiters (better known to you and me as "thing-a-ma-bobs that specify the beginning and end") "<%" and "%>" to enclose script commands. For example, the code below sets the value of the variable "MyFavTVShow" in the user cookie to "I Dream of Jeannie."

<%Response.Cookies("MyFavTVShow")="I Dream of Jeannie"%>

The scripting languages supported by ASP in turn support use of the If-Then-Else construct (something that will undoubtedly warm the hearts of all coders out there). Finally, you can embed some real logic into your HTML. For example, the following code from the IIS documentation shows how you can set the greeting shown based upon the time of day.

<FONT COLOR="GREEN">
<%If Time >= #12:00:00 AM# And Time < #12:00:00 PM# Then%>
Good Morning!
<%Else%>
Hello!
<%End If%>
</FONT>

I'm sure that you can think of something more interesting for your Web site—I'd hate to have to come up with all the clever ideas.

Built-In Objects

ASP includes five standard objects for global use:

  • Request—to get information from the user.

  • Response—to send information to the user.

  • Server—to control the Internet Information Server.

  • Session—to store information about and change settings for the user's current Web-server session.

  • Application—to share application-level information and control settings for the lifetime of the application.

The Request and Response objects contain collections (bits of information that are accessed in the same way). Objects use methods to do some type of procedure (if you know any object-oriented programming language, you know already what a method is) and properties to store any of the object's attributes (such as color, font, or size).

The Request Object

The Request object is used to get information from the user that is passed along in an HTTP request. As I mentioned earlier, the Request and Response objects support collections:

  • ClientCertificate—to get the certification fields from the request issued by the Web browser. The fields that you can request are specified in the X.509 standard.

  • QueryString—to get text such as a name, such as my favorite TV sitcom above.

  • Form—to get data from an HTML form.

  • Cookies—to get the value of application-defined cookie.

  • ServerVariables—to get HTTP information such as the server name.

The Response Object

The Response object is used to send information to the user. The Response object supports only Cookies as a collection (to set cookie values). The Response object also supports a number of properties and methods. Properties currently supported are:

  • Buffer—set to buffer page output at the server. When this is set to true, the server will not send a response until all of the server scripts on the current page have been processed, or until the Flush or End method has been called.

  • ContentType—to set the type of content (for example, text/HTML, Microsoft® Excel, and so on).

  • Expires—sets the expiration (when the data in the user's cache for this Web page is considered invalid) based on minutes (for example, expires in 10 minutes).

  • ExpiresAbsolute—allows you to set the expiration date to an absolute date and time.

  • Status—returns the status line (defined in the HTTP specification [http://www.w3.org/Protocols/] for the server).

The following methods are supported by the Response object:

  • AddHeader—Adds an HTML header with a specified value.

  • AppendToLog—Appends a string to the end of the Web server log file.

  • BinaryWrite—writes binary data (for example, Microsoft® Excel spreadsheet data).

  • Clear—clears any buffered HTML output.

  • End—stops processing of the script.

  • Flush—sends all of the information in the buffer.

  • Redirect—to redirect the user to a different URL.

  • Write—to write into the HTML stream. This can be done by using the construct:
Response.write("hello")

or the shortcut command:

<%="hello"%>

The Server Object

The Server object supports one property, ScriptTimeout, which allows you to set the value for when the script processing will time out, and the following methods:

  • CreateObject—to create an instance of a server component. This component can be any component that you have installed on your server (such as an ActiveX ).

  • HTMLEncode—to encode the specified string in HTML.

  • MapPath—to map the current virtual path to a physical directory structure. You can then pass that path to a component that creates the specified directory or file on the server.

  • URLEncode—applies URL encoding to a specified string.

The Session object

The Session object is used to store information about the current user's Web-server session. Variables stored with this object exist as long as the user's session is active, even if more than one application is used. This object supports one method, Abandon, which (believe it or not!) abandons the current Web-server session, destroying any objects, and supports two properties, SessionID, containing the identifier for the current session, and Timeout, specifying a time-out value for the session. One thing to bear in mind about the session identifier: It's not a GUID. It's only good as long as the current Web-server session is running. If you shut down the Web-server service, the identifiers will start all over again. So don't use it to create logon IDs, or you'll have a bunch of duplicates and one heck of a headache.

The Application Object

The Application object can store information that persists for the entire lifetime of an application (a group of pages with a common root). Generally, this is the whole time that the IIS server is running. This makes it a great place to store information that has to exist for more than one user (such as a page counter). The downside of this is that since this object isn't created anew for each user, errors that may not show up when the code is called once may show up when it is called 10,000 times in a row. In addition, because the Application object is shared by all the users, threading can be a nightmare to implement.

Where to Find More Information on ASP

There's already a lot more information about Active Server Pages technology readily available. The trick, of course, is knowing where to find it and what to look for.

The place to start is at the documentation (http://www.microsoft.com/iis/Support/iishelp/iis/misc/documentation.asp?A=5&B=3). (I can hear you groaning already. Stop it! Read, why don't you?)

Then take a look at the Internet Information Server Web site (http://www.microsoft.com/iis/default.asp). I suggest clicking on the cascading menus on the left-hand pane. Click the Learn About IIS header and highlight Active Server Pages.

If you are a developer, click on the box labeled Developer Tools & Samples (http://www.microsoft.com/downtrial/moreinfo/iissamples.asp) to see a host of samples written in different languages. The Developer Sample download shows you how one sample looks when written using Visual Basic 4.0, Visual Basic 5.0, Microsoft® Visual J++® 1.0, and Microsoft® Visual C++® 4.2 (MFC and ATL 1.1). This is particularly nice if you are trying to learn the different technologies.

The Microsoft IIS team has created a set of articles in the Server section of our Site Builder Network Workshop. These articles go into detail about the technologies associated with IIS and ASP. Armed with these articles, you can learn to create an active server component using ATL, how to manage sessions using ASP, how to use ASP in your international pages, and how to troubleshoot your ASP files.

The IIS team recommends the first book on ASP and IIS, Working with Active Server Pages (QUE Corporation, US $39.99), available through QUE's Web site (www.mcp.com).


ASP Technology Feature Overview

Ron Wodaski
Microsoft Corporation

August 27, 1998

Download doc

Contents

Introduction
Language Independence
Development with Scripts
Built-in and Installable Objects
Databases and Cookies
Transactions on the Web
Isolated Processes
Security
Creating Client/Server Web Applications

Introduction

This article provides an overview of Active Server Pages (ASP) technology, a technology made available by Microsoft® Internet Information Server (IIS). The ASP features described in this article are available in IIS version 4.0. After walking through the highlights of ASP features, I provide a section on how those features fit together to create client/server Web applications.

Although links to further information are provided in each section, I recommend further reading overall in the IIS 4.0 SDK.

Language Independence

Active Server Pages (ASP) technology is language-independent. Two of the most common scripting languages are supported right out of the box: Microsoft® Visual Basic® Scripting Edition (VBScript) and Microsoft® JScript®. Support for other scripting languages, such as Perl, is available. Whatever scripting language you use, you can simply enclose script statements in special delimiters for ASP. The starting delimiter is <%, and the closing delimiter is %>. Delimiters are discussed further in Development with Scripts.

VBScript

Using VBScript on the server in an ASP page isn't very different from using it in applications or on ordinary Web pages. Nearly all of the VBScript commands are available for use on the server. VBScript commands that interact with the user, however, are not available. For example, imagine a command that opens a dialog box on the server. No one is around to dismiss it, and the system can do nothing until someone dismisses it!

The VBScript statements that present user interface elements are InputBox and MsgBox. In addition, the VBScript function CreateObject is replaced by a method of the Server object. This is necessary to track the object instances on the server side. You can add comments to your script just as you normally do. However, you cannot add comments inside an output expression. An output expression is an expression or value that is evaluated and written to the Web page. It is contained within the delimiters <%= and %>.

JScript

The rules for using JScript are very similar to those for VBScript. The delimiters are the same, for example. As with VBScript, you cannot use user interface statements such as the Alert statement.

The way you use JScript on the server side is nearly identical to the way you use it on the client side. As on the client side, JScript on the server is case-sensitive. The rules for case in server-side scripts are:

  • The names of objects must be capitalized

  • Method and property names can be any case

  • When in doubt, use the case shown in the references in the DHTML, HTML & CSS area of SBN Workshop (click "show contents" on the home page).

You can also add support for other scripting languages, such as Perl, to IIS. See the IIS SDK documentation for details.

Development with Scripts

Scripting languages are great for creating applications quickly. Compared to formal programming languages, you generally need far fewer lines of script to accomplish a task. Now that Dynamic HTML and the Document Object Model have arrived, you can even combine server-side and client-side scripting to quickly develop a prototype of your ideas.

Or perhaps you simply want to serve Web pages that are sensitive to the features supported by a particular browser version. This applies to different versions of a single browser (Microsoft® Internet Explorer 3.x and 4.0, for example), different operating systems (Microsoft® Windows® and UNIX, for example), and to completely different browsers (such as Netscape Navigator and Internet Explorer). You can create scripts that are appropriate for each browser, and invoke the appropriate commands to serve pages uniquely suited to the features of any browser. The Browser Capabilities object provides detailed information about the features supported for each browser version. For example, you can design an application for Internet Explorer 4.0 that displays different HTML code for down-level browsers such as Internet Explorer 3.02, or for alternate browsers, such as Netscape Navigator. This enables you to maintain one set of content pages, with conditional browser detection for features on different browsers built right into each page.

You can do a lot of development with scripts. You can incorporate business rules, for example, into a complete client-server or multi-tiered business application such as a telemarketing sales application. Or you can simply verify that a visitor has been to your site before and recall preferences for that user.

HTML to the Rescue

One way to think about ASP pages is that they are really just HTML pages with extra stuff added. You can develop your basic pages the way you always have--as Web pages, and add the appropriate scripting for whatever browsers or business rules you require. You can:

  • Create Web pages that detect the browser in use, and display properly for that particular browser or browser version

  • Reuse HTML on a variety of pages, and avoid duplication of effort

  • Develop complete applications that work over the Web using forms and the advanced scripting potential of ASP

Converting to ASP

It couldn't be easier to convert an existing HTML page to an ASP page -- just change the extension from .htm/.html to .asp. Of course, it won't be a meaningful ASP page until you add some scripting commands. The .asp filename extension enables IIS to parse and execute the scripts in your files.

Keep in mind that IIS and ASP run on Microsoft® Windows NT® Server. You can create an intranet that functions in your local area network, or serve pages across the Internet by connecting your server to the Internet. You can integrate any of the IIS 4.0 features into your ASP pages, such as using Microsoft® Transaction Server (MTS) to safely commit or abort transactions that span multiple computers.

Easy Delimiters for Several Languages

If you have any experience with Visual Basic, Visual Basic for Applications, VBScript, JScript, or JavaScript, you can build Active Server Pages with a scripting language you already know. This means you can be up and running with ASP quickly. The key is to place ASP scripts in delimiters:

  • Embed your script commands within the delimiters: <% and %>

  • Feed variables to the page using a special form of the starting delimiter:<%=

For example, to add the current date and time to a page:

<HTML>
<BODY>
<P>The current date and time on the server: <%= Now %>.</P>
</BODY>
</HTML>

The result will look like this:

The current date and time on the server: 8/24/98 11:40:02 AM.

You can use various script statements to control what appears on the Web page. The following example shows how to change the output based on the time of day, but you can use any conditional statement with whatever criteria (user's name, current database record, script language supported by the visitor's browser, and so forth) to control output to the Web page.

<%
If Time >= #12:00:00 AM# And Time < #12:00:00 PM# Then
strGreeting = "Good Morning!"
Else
strGreeting = "Hello!"
End If
%>
<P><%= strGreeting %></P>

Two styles for using delimiters are shown in the above code sample. The five lines of code that constitute the If statement are set off from the delimiters using line breaks. The variable strGreeting in the last line of the sample is a single line, and is enclosed within delimiters on the same line. As a general rule, your code will be more readable if you follow these two style rules:

  • If there is more than one line of script, put the starting and ending delimiters on separate lines. Indent the code from the left margin at least two spaces for clarity.

  • If there is only one line of script, put the starting and ending delimiters on the same line. Leave a space before and after the script for clarity.

For more information, see ASP Programming Conventionsthe Site Builder Network Workshop.

Built-In and Installable Objects

Most of the functionality you can build into an ASP page comes from objects on the server. IIS 4.0 comes with some built-in objects, as well as a number of installable objects. You can also use objects created by a developer you know, or create and use your own objects.

Built-In Objects

Six objects come built-in with IIS 4.0. These objects enable your pages to communicate effectively with the server, the application, the current session, and the user:

  • Request object -- Gets information from the user. You can get FORM data, read cookies, and so forth.

  • Response object -- Sends information to the user. You can send text to the page, redirect to another URL, and set cookies.

  • Server object -- Interacts with the server. You can access a database, read files, and find out about the capabilities of the browser.

  • Session object -- Enables you to manage information about the current session (each user has one session per open browser).

  • Application object -- Will store information for all sessions.

  • ObjectContext object -- Used for committing and aborting transactions (new in IIS 4.0)

Using Built-In Objects

Use the built-in objects just like any other object in a script; by accessing properties, methods, and events. For example, the general format for using the Request object looks like this:

Request[.Collection](variable)

Here is the Request object in action, being used to write a form field's value on a Web page:

<P>The value of 'strFirstName' is
<%= Request.Form("strFirstName") %></P>

The collections available for the Request object are:

  • ClientCertificate -- The values of fields stored in the client certificate that is sent in the HTTP request

  • Cookies -- The values of cookies sent in the HTTP request

  • Form -- The values of form elements in the HTTP request body

  • QueryString -- The values of variables in the HTTP query string

  • ServerVariables -- The values of predetermined environment variables

Installable Objects

IIS 4.0 comes with a variety of installable objects, and you can also use your own custom objects on the server side. The installable objects include:

  • Ad Rotator -- Rotates advertisements displayed on a page using a schedule

  • Browser Capabilities -- Gives you access to information about the client browser's capabilities, type, and version

  • Database Access -- Provides access to databases using ADO (Microsoft® ActiveX® Data Objects)

  • Content Linking -- Creates tables of contents for Web pages, and links them sequentially like the pages of a book

  • File Access -- Provides access to file input and output

  • Collaboration Data Objects for Windows NT Server -- Provides the ability to send and receive messages from your Web page

Databases and Cookies

Data-Driven Web Pages

IIS 4.0 and ASP make it easy to access data and put it on a Web page. You can simply display data from an ODBC-compliant database, or you can use ASP to make decisions about what to display on Web pages. Here's the process:

  1. Create an ODBC Data Source -- Specify a Data Source Name (DSN) in the server registry or in a file, and grant access to all users, specific users, or groups.

  2. Make the Connection -- Use the Connection object to point to a DSN, and create a Recordset object to gain interactive access to data.

  3. Execute Queries -- You can construct queries using standard SQL syntax. You can submit queries using Connection methods, Recordset methods, or with the Command object.

  4. Reissue Queries -- You can use the Command object to change query parameters and quickly resubmit the query. For example, you can INSERT a record into your database with a set of values, reset the values for the next record, insert it, and so on. The Command object makes it easy to construct valid queries, too, because it uses a parameter-driven interface that is easy to set up and maintain.

  5. Access Data Using Forms -- With forms, your users can perform queries, read data, change data, add records, and so forth. Thus you can create a complete client-server solution. Using the transaction features built into IIS 4.0, you can even create multi-tiered business solutions on the Internet.

Cookies

You can use cookies, as well as session and application variables, to store and retrieve information. IIS makes cookie values available to you with the Request object's Cookies collection. You can set cookie values with the Response object. Because IIS sends all header information before the document content, you must set cookies before the <HTML> tag. For example, to check the value of a cookie, you can use this code:

<%
Dim strNameCookie
strNameCookie = Request.Cookies("nameCookie")
%>

and then set some session variables based on the cookie value. You can use default values if there is no nameCookie, or the existing cookie values if there is a nameCookie:

<%
If strNameCookie = "" Then
Session("strDefaultPage") = ""
Session("strDefaultLevel") = "1"
Else
Session("strDefaultPage") = Request.Cookies("pageCookie")
Session("strDefaultLevel") = Request.Cookies("levelCookie")
End If
%>

For more information, see Data-Driven Web Applications.

Transactions on the Web

PCs have become much more powerful recently, and so have servers. This makes multi-tiered client/server computing possible. By arranging functionality into neat packages called transactions, it is possible to involve more than just the client and server computers in the process. Thus the key to using the Internet for major application development is support for these transactions. This isn't easy to do, because transactions typically involve processes on multiple computers, and a method is needed to coordinate all those computers safely and reliably.

Transactions Explained

In very simple terms, a transaction acts like an international diplomat. For anything to happen in a transaction, every single party to the transaction has to agree that it is okay to proceed. The following table shows the parallels between the components that make up MTS and international diplomacy.

MTS Feature | Description | Diplomatic Equivalent
IIS 4.0 and MTS | Provide a runtime environment for transaction processing. | Deciding on the negotiation location and size of the table to be used for the negotiations on the new treaty.
Distributed Transaction Coordinator (DTC) | Setting up coordinator software to oversee transactions. | Selecting a diplomat to chair the meetings about the treaty.
Distributed Processing | Checking to make sure that every computer involved is ready to process a transaction. | Robert's Rules of Order.
Transaction List in the DTC | Monitoring transactions to make sure they process correctly. | Sending diplomatic pouches to and fro.
Forcing transaction completion | Manually committing or aborting transactions. | Shuttle Diplomacy used when talks break down and confusion reigns.

Transactions on the Internet

Transactions are the key to client/server and multi-tier applications on the Web. Microsoft Transaction Server monitors all aspects of a transaction, across all of the computers that are involved in the transaction. The Distributed Transaction Coordinator (DTC) monitors every aspect of every transaction on every computer involved. This two-way communication insures that the transaction gets completed, and that records of the transaction agree across all computers.

For example, consider an order entry system consisting of these key parts:

  • Database Server -- A computer with the database describing the current inventory for a large, Midwestern wholesale company. The computer is physically located at company headquarters in Indianapolis, IN.

  • Transaction Server -- A computer running Microsoft Transaction Server and IIS 4.0. The computer is physically located at the company's development satellite office in Peekskill, NY.

  • Business Rules #1 -- A computer that runs the company's accounting and shipping software, as well as software to interface with the inventory database. The computer is physically located at the New York City offices of a firm that processes the company's financial information.

  • Business Rules #2 -- A computer that serves the ASP Web pages as well as several software programs that interact with the inventory database, the accounting software, and the shipping software. The computer is physically located at the company's development satellite office in Peekskill, NY.

  • Data Entry -- There are over 250 computers used for data entry. The computers are physically located at multiple sites, including laptops carried by salespeople on the road.

  • Data Monitoring -- A computer on the desk of the CEO, which accesses data across a wide variety of databases and synthesizes the data into digestible summaries. The computer is physically located at company headquarters in Indianapolis, IN.

In the past, such a system would require extensive development time and installation of a variety of networking connections. By using Microsoft Transaction Server, IIS 4.0, ASP, and the Internet, development time and costs are greatly reduced. Cost reductions result from:

  • Simplified coding -- ASP pages and scripts enable the company's existing developers to create the bulk of the functionality required. At the same time, interfaces to specialized software are built using conventional Windows technology. IIS 4.0 ties all the pieces together.

  • Reduced interconnection hardware -- The Internet supplies ready-made connectivity.

  • Simplified maintenance -- ASP and components work cooperatively, enabling you to script or program each portion of the solution using the appropriate tool.

  • Quicker prototyping -- The combination of scripting and Web pages enables the development team to deliver prototypes to the management team on a regular basis. Changes are integrated almost immediately.

For more information, see IIS 4.0 and MTS 2.0.

Isolated Processes

IIS 4.0 enables you to run each of your Web applications in an isolated process on the server. To isolate an application, select "Run in separate memory space" on the application directory's property box.

What Does Isolation Do?

An isolated application runs in its own memory space on the server. This memory is available only to that specific application; no other application can access that memory space. If a problem develops in the application, there is no way for it to "invade" the memory space of other applications, or of any other software running on the server. Isolation prevents one application from dragging down other applications (or the server) if it crashes. It's a little like having multiple computers -- virtual machines -- running on one server.

Performance

Using isolated processes affects the performance of your applications. There are four scenarios available for process isolation:

Figure 1. Process isolation options

For more information, see the Active Server Components articles in the Server Technologies and Component Development (click "show contents" from default.asp) sections of SBN Workshop.

Security

With the Security features of Windows NT and IIS, you can implement a security plan that is customized and very safe.

Access and Identification

Windows NT Server 4.0 provides a high level of security for your Web services. The access permissions of Windows NT File System (NTFS) provides the foundation for Web security. You can define access for individual users or for groups of users, and provide a default level of access for general users. With NTFS, you can:

  • Secure Your Files -- Create user accounts and assign users to groups

  • Set Access Permissions -- Set access permissions to files and folders for appropriate users and groups

  • Configure Anonymous Access -- Determine how anonymous users will interact with your site

  • Grant and Deny Access -- Determine which computers, groups of computers, or domains will have access to your site, folders, and/or files

  • Set Web Server Permissions -- Determine how users will interact with specific FTP and Web sites

  • Use ASP for Access Control -- Enable basic authentication, and use scripting to log on users

  • Control Database Access -- Use features of your database software and NTFS to control access to your database content

In addition, you can provide content ratings to warn users about objectionable content, and configure your Web server to use the powerful security features of Secure Sockets Layer (SSL).

Authentication

Once you have set permissions for your site, folders, and files, there are several ways to authenticate visitors to your Web site.

  • Basic Authentication -- An industry-standard technique involving a browser-generated dialog for user authentication. While supported, this is not, in itself, a secure method of protecting your content. It is often used when only simple security measures are deemed necessary.

  • Challenge/Response Authentication -- Supported by Internet Explorer version 2.0 and later, this method verifies users without requiring the actual transmission of names and passwords across a network. This method works well on an intranet.

  • SSL Client Certificates -- Secure Sockets Layer 3.0 supports authentication by checking the contents of an encrypted digital ID (also called a certificate). The browser submits the ID during the logon process.

  • Certificate Mapping -- Windows NT Server 4.0 can map a client certificate to a Windows NT logon ID.

  • Certificates for Anonymous Users -- You can use certificates to determine which users are allowed an anonymous logon ID to the server.

  • FTP Authentication -- Set up user name/password for logging on via FTP.

Encryption

Encryption is a process that scrambles your content so that it is very, very difficult to unscramble -- unless you have a key, in which case it's easy and straightforward. When a user establishes a secure connection with your server, encryption is used to make the content unreadable to anyone who might be able to intercept it. This allows private information to stay private as it moves back and forth between client and server. You can use the Key Manager to create, import, and export SSL (Secure Sockets Layer) encryption keys.

For more information on security, see the Security Technologies (click "show contents" on default.asp) area of SBN Workshop and the Microsoft Security Advisor site. For more information on IIS security, see IIS Security and the IIS Security FAQ.

Creating Client/Server Web Applications

ASP technology makes it easy to create complete Web-based applications. You don't have to use the full breadth of ASP technology on your Web pages, of course; you can simply use ASP as a template language to create flexible, easy-to-maintain Web pages. Or you can create a client/server application: define a directory for the application, and all files in that directory (and in all of its sub-directories) exist as an application. Pages can communicate with each other, or with the application object. This is possible because you can persist selected information across a user session and/or the entire application as needed.

A typical application can include Web pages, databases, controls, server-side scripts, client-side scripts, multiple servers, and transactions. The first visitor to the application starts the application, and each visitor has a unique session. You can create just about any kind of application because of the multiple levels of control available to you: scripts, built-in ActiveX components, third-party components, custom components of your own, databases, and transactions.

Other key features of applications include:

  • Start/End Events -- You can start and end applications as needed, with complete control over what occurs at both events. You can define subs or functions that run at the start and end of an application, enabling you to configure the application automatically each time it starts, or clean up each time it ends.

  • Isolated Processes -- You can run each application in its own process on the server. This protects applications from each other -- should one application go down, it would not take the server or other applications with it.

  • Application Object -- The Application object has its own properties and methods. You can set or read properties and use methods on any page that is part of the application. You can set variables with application scope when the application starts, and change them at run time as needed to indicate the application's current state. For example, you could store the number of current sessions in the Application object, and increment/decrement it as visitors come and go from your site.

For more information, see Data-Driven Web Applications.

Sessions and Web Applications

The Session object enables you to communicate effectively between the pages of your application, and to store information about visitors so you can easily retrieve it on their next visit.

Each visitor to your site has a Session object with a unique session ID. You can use the Session object to maintain information about that user during the session. The session ID is stored in a cookie, and you can add/read your own cookies as needed. You can use the session ID cookie to rebuild session information from one session to the next. For example, if a user visits your site and establishes preferences that you store in a database, you can use the session ID cookie to read the correct database information every time that visitor returns to your site. This makes it easy to create a "personalized" Web site.

You can store variables in the Session object that have session scope. That means you can access the variable from any page in the application that the user visits. You can also establish handlers for the session start and end events, integrate forms into your sessions, and so forth.

For more information, see Managing Session State in a Web Farm.


COM Objects in ASP

Wayne Berry

Editor, "15 Seconds"

Microsoft Corporation

January 1, 1998

Contents

Introduction
The Basics
Properties
Methods
Arguments
Collections
The Default Method or Property
Instantiating an Object
Built-in COM Objects
The Documentation
Read and Write Properties
Read-Only Properties
Optional Method Arguments
Summary

Introduction

If you are an Active Server Pages (ASP) developer, you have already used Component Object Model (COM) objects to create your ASP pages. However, unless you have developed COM objects or read a detailed book on COM, you might not know about the multitude of COM objects that you can use in ASP. Also, without sufficient COM knowledge, you might not be able to infer the methods and properties that exist for those objects when reading the documentation. One of the wonderful things about COM is that once you learn the standards and restrictions, you can quickly learn to implement other COM objects. In this tutorial, we will explain how COM works from the perspective of someone who writes Microsoft® Visual Basic® Scripting Edition (VBScript), and this knowledge will help you master the world of COM.

This tutorial will demonstrate and describe the basics of COM to those familiar with VBScript and COM objects, and is especially useful to those who have used COM objects like Microsoft® ActiveX® Data Objects (ADO) without knowing that they are COM. This tutorial is for you if have ever wondered any of the following:

  • What is the difference between a property and a method?

  • Does a property take an argument?

  • What does it mean for a property to be read-only?

  • What is a collection object?

  • What properties are standard with every collection object?

  • Why can I index a collection object without calling a method?

  • How many COM objects can there be in a single DLL?

The Basics

COM (http://www.microsoft.com/com/) is the standard for the interface to objects. By definition, COM objects have only methods and properties; there are no other interfaces. There isn’t much difference between properties and methods from a programmer’s standpoint: Methods can take arguments, properties can’t. Properties can be read/write; methods -- if they return a value -- are read-only.

Component designers use methods and properties for different functionality. Properties usually represent some aspect of the object’s state, whereas a method can be a function that performs regardless of whether the object’s state is involved.

Properties

Properties do not take any arguments and are usually used to describe or set the state of an object. All properties return a value, however some properties are read-only, and some are read/write. Here is an example of the VBScript syntax for reading a property:

Example 1

value = object.property

Note there are no parentheses, not even a blank set; that is, ( ). Here is the Visual Basic syntax for setting a property:

Example 2

object.property = value

Methods

Methods can return values and take arguments. They are most often used to initiate an event within the object. Methods can be used to set values, but only when passing the value through the argument list. If a method returns a value but doesn’t take an argument, the syntax will be:

Example 3

value = object.method()

Note in Example 3 that the method has a set of blank parentheses. Methods that have a return value must have arguments encapsulated in parentheses. For example, the Connection object has an Execute method that returns a RecordSet object. Here is an example:

Example 4

Set RS = Conn.Execute(“SELECT * FROM TABLE”)

Methods that do not return values do not have parentheses around the arguments. For example, the Close method of the Connection object is not encapsulated in parentheses:

Example 5

Conn.Close

Arguments

Methods can take one or more arguments, or take none at all. However, arguments might be optional. If they are, you do not have to enter anything for an argument. Once one argument is optional, all arguments following it are also optional. For example, if arguments one and two are required, and three is optional, argument four has to be optional. A good example of an optional argument method is the Open method of the Connection object. The Open method has eight optional arguments. The first three are for establishing the database and the logon information. You can call the Open method as in Example 6:

Example 6

Conn.Open “DSN”,”sa”,””

This indicates a DSN of “DSN”, a logon of “sa”, and a password of “”.You can also call the Open method as in Example 7:

Example 7

Conn.Open "driver=SQL Server;server=yourServerName;uid=someUID;pwd=somePWD;database=someDatabase;"

Note that we used three of the optional arguments in Example 6 and we used only one in Example 7, with the same result.

Calling the arguments by delimiting with the argument and leaving it blank causes the method to execute with nulls instead of the optional argument’s default values. In Example 6, the default value of the optional arguments is used. In Example 8, nulls are used for the optional arguments:

Example 8

Conn.Open “DSN”,”sa”,””, , , ,

Example 8 calls the optional methods with null values, which is different than Example 6.

Collections

Collections are objects that represent a set of objects. All collections have predefined methods and properties. A collection object has an Item method, a Count property, and a _NewEnum method. A collection can also create objects of the collection type. In other words, if a particular object can be grouped in a set, that object will have a collection object that can create an instance of an object within the set. For example, a Drives collection object will contain a set of drives that can represent all the drives on a particular computer.

The Count property returns a LONG value that specifies how many objects are in the collection. By passing a LONG value -- that is between one and the value returned by the Count property -- to the Item method, the collection method will return the object in the set that is associated with that position. Accessing an item in an array works similarly.

The _NewEnum method enables a programmer to iterate through the collection in a For…Next statement. Example 9 shows _NewEnum in action:

Example 9

For Each Object in Collection
   …
Next Object

Note that the _NewEnum method is not referenced within the syntax of the statement in Example 6. This is because the _NewEnum method has a special index that is used for the For…Next statement. In fact, all methods and properties in a COM object are indexed and certain indexes are used for particular tasks. For example, the zero index is used for the default method or property.

The Default Method or Property

The method or property that has the COM index of zero is called the default property. Visual Basic enables a programmer to not use the regular method/property syntax when calling the default value; you can leave the syntactical call to the method/property off altogether. For example, the default method in all collections is the Item method. Example 10 shows how to call the Item method:

Example 10

Set Object = Collection.Item(2)

This would get the second item in the collection and assign the object variable to that object. Because the Item method is the default method, you can also call the Item method as in Example 11:

Example 11

Set Object = Collection(2)

Note that both the period and the actual name of the method are missing; only the argument to the method remains.

Instantiating an Object

To create an instance of a COM object in ASP, you can use a statement like the following:

Example 12

Set Object Server.CreateObject("SMUM.XCheck.1")

There is only one argument to the CreateObject method of Server that is the ProgId (the program ID). The ProgId is assigned by every component vendor to uniquely identify the COM object. To create an instance of the COM object, you must know the ProgId of the COM object.

There is another way to get an instance of a COM object. You can have another COM object create the object and return the newly created object to you. This is how a collection works. You call the Item method of a collection, and a COM object is returned that represents the subset of the collection, which you index. Whenever a COM object is returned by another object, you must preface the statement with Set as in Example 13:

Example 13

Set Object = Collection.Item(2)

Because Server is a COM object, Examples 12 and 13 are much alike. They both return COM objects with a call to another COM object. The difference is that the CreateObject method of the Server object can return any COM object, and the Item method can only return COM objects that are stored in the collection. If you need to have a COM object to create another COM object, where did the Server object come from? ASP has a set of built-in COM objects that solve this chicken-or-the-egg problem.

Built-in COM Objects

There are six built-in COM objects in the ASP environment:

Server

Request

Response

ObjectContext

Application

Session

The difference between these COM objects and the others is that you do not need to create an instance of these objects to call them. Built-in objects are present in the page without instantiation. They adhere to all the other rules of COM and have their own methods and properties. You do not need to know their ProgIds because you don't have to call the CreateObject method to instantiate them.

ProgId

If one of the major ways to create a COM object is by using the CreateObject method, knowing the ProgIds of the objects you are creating is very important. So where are the ProgIds located? The component vendor should supply the component ProgIds as part of the documentation.

However, not all ProgIds are supplied, because a vendor doesn’t always want you to create an instance of the object using the CreateObject method. Some objects inherit properties from the object that creates them, so if they are not created from calling a method in that object they are not initialized correctly. For example, creating an instance of an ADO Field object would not do you much good without going through the RecordSet object, because the ADO Field object would not contain any data unless you went through the RecordSet object.

The Documentation

Now that we have established the differences between methods and properties along with their different attributes, we need to understand how the documentation for the objects represents these attributes. For example, we are going to look at 15 Seconds’ component section, which is in the same format as the IIS 4.0 component documentation.

Read and Write Properties

A good example of a read/write property is that of the PhoneTranslate property of the XCheck object, shown here in Example 14:

Example 14

object.PhoneTranslate[= value]

Note the value syntax -- this indicates a property to which you can write. The brackets denote that the property is optional, that is, you do not need to set the property to use the object. Here’s a pointer to the full documentation: http://www.15seconds.com/Component/Property.asp?ComponentId=1&PropertyId=8 on the 15 Seconds site.

Read-Only Properties

A good example of a read-only property is the Expires property of the ASPMail object.

Example 15

object.Expires

Note that, unlike Example 14, there is no equal symbol, which indicates that this property is read-only. Here’s a pointer to the full documentation: http://www.15seconds.com/Component/Property.asp?ComponentId=1&PropertyId=8 on the 15 Seconds site.

Optional Method Arguments

A good example of the optional arguments is the SendX method of the OCXMail object. The documentation syntax can be seen here in Example 16:

Example 16

object.SendX(mailserver[, fromName[, fromAddress[, priority[, returnReceipt[, toAddressList[, ccAddressList[, bccAddressList[, attach[, messageSubject[, messageText]]]]]]]]]])

Note that the only required argument is in the mailserver argument. All the others, as indicated by the brackets, are optional. Here’s a pointer to the full documentation: http://www.15seconds.com/Component/Method.asp?ComponentId=20&MethodId=125 on the 15 Seconds site.

Summary

With a fundamental understanding of COM, its abilities, and good documentation, you can expand the flexibility of your ASP programming. Take the information that you already know about programming Internet Information Server (IIS) objects, like Session objects and ADO, and expand on that by adding more COM objects to your repertoire. Third-party COM objects will enable you to expand your Active Server applications and accomplish tasks rapidly by leveraging the Component Object Model.

15 Seconds (http://www.15Seconds.com) is a free Internet resource for developers working with Microsoft Internet Solutions and product lines of Sign Me Up Marketing (http://www.signmeup.com) SMUM a leader in Internet marketing. Established in 1996, SMUM is a Seattle-based business owned by Wayne Berry, a well-known global programming consultant and co-author of Active X Programming Unleashed, NT4 Registry, IIS 4.0 Special Edition.


Managing In-Process COM Components Using MTS and ASP Technology

Yuri Lausberg

Microsoft Corporation

January 4, 1999

It may be necessary to have your in-process COM components run on a different server than Microsoft® Internet Information Server (IIS), using Active Server Pages (ASP) technology to create an instance of your object and execute your application. An example of this would be the creation of a distributed (n-tier) environment in which you separate your components from the main Web server, therefore making your application scalable while taking system load off the main Web server.

In this article, I would like to present a solution for managing your in-process COM components, using Microsoft® Transaction Server (MTS), a sample Microsoft® Visual Basic® component and ASP technology. We will take advantage of an n-tier architecture on two different servers running MTS. One server will host the in-process COM components under MTS, while the other server will run IIS to handle the incoming HTTP requests.

An MTS component is a type of COM component that executes in the MTS run-time environment. In addition to the COM requirements, MTS requires that the component must be a dynamic-link library (DLL). Components that are implemented as executable files (.exe files) cannot execute in the MTS run-time environment. For example, if you build a Remote Automation server executable file, you must rebuild it as a DLL.

Exercise caution when registering a standard COM component (one developed without regard to MTS) to execute under MTS control.

First, ensure that references are safely passed between contexts.

Second, if the component uses other components, consider running them under MTS. Rewrite the code for creating objects in these components to use CreateInstance.

You can call MTS components from ASP files. You can create an MTS object from an ASP file by calling Server.CreateObject. Note that if the MTS component has implemented the OnStartPage and OnEndPage methods, the OnStartPage method is called at this time.

You can run your MTS components in-process with IIS, but be aware that if MTS encounters an unexpected internal error condition or an unhandled application error, such as a general-protection fault inside a component method call, it immediately results in a failfast, thus terminating the process and IIS.

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. Pushing components means creating remote component entries on remote computers. Once the remote component entries are created, you have to add those component entries to your Remote Components folder on your local machine (pull the components).

The following diagram illustrates pushing and pulling components to configure a remote computer running MTS.

You can push and pull components only if a shared network directory has been established for storage and delivery of DLLs and type library files. (You can choose any shared directory as long as the component files are contained within one of the folders or subfolders of the shared directory.) The MTS Explorer will automatically locate available shared network directories on servers. On a given server you can have multiple shared directories to access different sets of component files.

Requirements

  • MTS needs to be installed on both local and remote machines.

  • You must log on using a Microsoft® Windows NT® account that is a member of the Administrators role of the System Package on the target computer.

  • The target computer's System Package identity must map to a Windows NT account that is in the Reader role for your System Package.

  • Security must be enabled for the System Package on both computers.

MTS Packages

Before MTS can host an in-process component in its process space, it needs to have a package created. The package, which can easily be created in the MTS Explorer, is a collection of components that run in the same process.

Creating packages is the final step in the development process. Package developers and advanced system and Web administrators use the MTS Explorer to create and deploy packages. You use the Explorer to implement the package and component configuration that is determined during development of the application.

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 (such as an application fatal error), the other package can continue to execute in its separate process.

A big advantage of using MTS packages is that you can specify a package identity, and all the components contained in the same package will use that identity when accessing and launching its components. This is very useful when the authenticated user (e.g., anonymous) does not have sufficient permissions to access and launch certain components.

On the local machine:

We wrote an Microsoft® ActiveX® DLL with Visual Basic, using the following class and one method that returns the user context by calling the GetUserName API:

Private Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
Public Function WhoAmI() As String
   Dim sBuff$
   Dim lConst&
   Dim lRet&
   Dim sName As String
   lConst& = 199
   sBuff$ = Space$(200)
   lRet& = GetUserName(sBuff$, lConst&)
   WhoAmI = Trim$(Left$(sBuff$, lConst&))
End Function

After compiling and registering the component on the local machine, we need to create an empty package to host the remote component within the MTS explorer:

To create an empty package

  1. In the left pane of MTS Explorer, select the computer for which you want to create a package.

  2. Open the Packages Installed folder for that computer.

  3. On the Action menu, click New. You can also select the Package Installed folder and either right-click and select New and then Package from the menu, or select the Create a new object button on the MTS toolbar.

  4. Use the Package wizard to install either a pre-built package or to create an empty package. If you create an empty package, you must add components and roles before it will be functional.

  5. Click the Create an empty package button.

  6. Type a name for the new package, and click Next.

  7. Specify the package identity in the Set Package Identity dialog box, and then click the Finish button.

The default selection for package identity is Interactive User. The interactive user is the user that logged on to the server computer on which the package is running. If nobody is logged on to that server computer, any requests to execute this package will fail, therefore it is recommended to specify a different user. You can select a different user by selecting the This user option and entering a specific Windows NT user or group.

In many deployment scenarios, it is preferable to run a package as a Windows NT user account. If a package runs as a single Windows NT user account, you can configure database access for that account rather than for every client that uses the package. Permitting access to accounts rather than individual clients improves the scalability of your application.

When you would like to access network resources within the same Windows NT domain, you’ll need to specify an existing Windows NT domain user account; otherwise, you’ll have to copy the local Windows NT user account onto the each network resource you’d like to access.

By defining an identity, you are able to change user context between the user making the request and the user running the component. This way you can overcome the “double-hop” problem when using NTLM Authentication on the server.

On the remote machine

Once the local component entries are created, you have to add those component entries to your Remote Components folder on your remote machine (pull the components).

To add remote components, you must first add the appropriate computer or computers to your MTS Explorer and then add your components to the remote computer’s Remote Components folder.

Be sure that the remote components you want to add to your server are not already be registered on that server. This usually happens when you’re migrating from a centralized environment using only one server to a distributed environment using multiple servers. You can easily unregister existing components by using the regsvr32 command with the /u switch.

To add components to the remote computer's Remote Components folder

  1. Add the remote computer by selecting the Computers folder and clicking New in the Action menu.

  2. Type a name for the computer you want to add, and then click OK. If you do not know the name, you can click the Browse button to select a computer.

  3. In the MTS Explorer, open the Remote Components folder of the computer to which you want to add remote components.

  4. On the Action menu, click New. You can also right-click and select New and then Component from the menu.

  5. In the dialog box that appears, select the remote computer and package that contain the component you want to invoke remotely.

  6. From the Available Components list, select the component that you want to invoke remotely, and click the down arrow (Add). This adds the component and the computer on which it resides to the Components to configure on box. If you click the Details checkbox, the Remote Computer, Package, and Path for DLLs are displayed in the Components to configure on box.

  7. Click OK.

Note When you add components that are stored on a remote computer, the required files are stored in the \install directory\remote directory. (The default MTS installation directory is \Program Files\MTS.)

Active Server Pages

Now that we have done all the necessary steps to register the component with MTS, we can write an ASP file with the following code written in Microsoft® Visual Basic® Scripting Edition (VBScript):

<%@ Language=VBScript %>
<%
   Set oWhoAmIObject = Server.CreateObject ("WhoAmIComponent.WhoAmIClass")
   Response.Write "Who Am I: " & CStr(oWhoAmIObject.WhoAmI()) & " <BR>"
   Set oWhoAmIObject = Nothing
%>

In this script we create an instance of our component using Server.CreateObject with the corresponding PROGID. You can find this PROGID in the MTS explorer under Remote Components. Now that we have created an instance of our object, we can call our method and write the results back to the client. We need to be sure that after usage of our object instance, we do our own clean up by setting the object to Nothing.

Note We are instantiating our object in Page Scope. Using Page Scope will prevent us from using more system resources than necessary; it will create and destroy the object within the running page, therefore releasing the used memory and threads. If you need to maintain state between HTTP requests, you should still use Page Scope to create instances of your objects, but store state information such as variables into Session Scope. Another alternative to maintain state is to store variables in hidden form fields.

The last thing we need to do is to save this ASP file to the virtual directory on the IIS machine. Then we are good to go.

We’re now able to execute this ASP file from a browser:

Our request is being executed on the IIS machine, which in turn will make a request to MTS to create an instance of our object. Since MTS has this component registered as a remote component it will call the MTS package running on our local server, which will load the requested DLL into its memory space, create an instance of the requested object, and have the ASP code execute methods on it. Finally, we release our object instance, and we have finished our page.

For other references on this topic, see the Microsoft Knowledge Base articles “Frequently Asked Questions for Transaction Server,” (http://support.microsoft.com/support/transaction/content/faq/default.asp), especially “Q185184: Info: Components That Do Not Support Transactions” (http://support.microsoft.com/support/kb/articles/q185/1/84.asp). Also, check out the Site Builder Network article “Authentication and Security for Internet Developers” (http://www.microsoft.com/workshop/server/feature/security.asp), by Scott Stabbert.


IIS 4.0 and MTS 2.0: Technology for the Web

James Utzschneider
Microsoft Corporation

February 6, 1998

Contents
Introduction
What Is MTS, and Why Combine It with IIS?
Using IIS and MTS to Create a Transaction for a Web Page
Web Server as Application Platform

Introduction

When Microsoft shipped Internet Information Server (IIS) 3.0 one year ago, it introduced Active Server Pages (ASP). ASP enables Web developers to write scripts that run on the server or the client. This technology makes it easy for Web developers to build server applications that work with any Internet browser. An Active Server Page consists of HTML, scripts, and Component Object Model (COM) components that developers combine into a Web application. ASP also separates presentation logic from business logic, enabling three-tier business applications on the Web. (For more information on ASP, see What is ASP? and the ASP Overview.)

Microsoft® Transaction Server (MTS) 2.0 is integrated with IIS 4.0, which recently shipped. This integration of MTS and ASP makes ASP much more powerful without changing the programming model that made it so successful. This article talks about why MTS was integrated into the IIS architecture, describes the three major benefits of this integration, and shows how easy it is to develop server-side components that use MTS.

What Is MTS, and Why Combine It with IIS?

MTS is a new kind of middleware, software that makes it easier to build and scale distributed applications running on servers. For example, banks, manufacturers, telephone companies, and other large enterprises have run their most important business applications on software like Transaction Processing (TP) Monitor software like IBM’s CICS and IMS. A TP Monitor is a type of middleware that enables business applications to scale to support a large number of users and run reliably. TP Monitors do this by acting as big application multiplexors; that is, they enable system resources like processes and threads to be shared across thousands of concurrent users. Applications using TP Monitors run reliably through the use of transaction protocols that ensure that work executes properly and writes to disk accurately.

Unfortunately, developers cannot use modern programming languages like Microsoft® Visual J++® or Microsoft® Visual Basic® to write applications that run on traditional TP Monitors. To make TP Monitor services easier to use for a broad pool of developers, Microsoft turned to Object Request Brokering (ORB) technology based on COM.

ORBs, like TP Monitors, are a type of middleware. ORBs make the development and deployment of distributed applications built from component software easier. ORBs create objects, know where they are, and provide services separate from the code. This eliminates network programming and object management from coding.

In combining TP Monitor technology with ORB technology, MTS pioneered a new type of middleware engine that the Gartner Group called an Object Transaction Monitor, or OTM. OTMs integrate the scalability and reliability services of a TP Monitor with the distributed object brokering services of an ORB. In short, MTS serves as a container for COM components running in the middle tier of a three-tier application.

Easier Server Programming

Developing server-side components that use MTS is relatively easy for an experienced developer. MTS components are implemented as “single user” COM DLLs, where developers focus on implementing business functions and not on complex, multiuser programming issues like thread creation or termination and user concurrency in writing middle-tier business logic.

Developers can write these components in any language that supports COM, including Visual Basic, Microsoft® Visual C++®, Visual J++, Sybase PowerBuilder, Borland Delphi, MicroFocus COBOL, and Oracle Objects for OLE.

MTS enables components to automatically execute within transactions, a software mechanism that ensures that software programs execute reliably. At a low level, MTS uses a two-phase commit protocol to coordinate the participants in the transaction. For example, a Web application designed to sell books on the Internet may involve five components and three database tables. MTS ensures that all eight of these elements always work together and complete a task, or that none of them do, to make sure that the Web site never has inconsistent or incorrect results.

Unlike traditional TP Monitors, MTS does not require developers to insert complex transaction processing code directly into their programs. Developers do not have to write begin transaction, end transaction, enlist specific databases on a transaction, or track transaction IDs across different components or processes. With MTS, transaction support simply becomes a type attribute of component’s class that is configured using the MTS Explorer. The MTS Explorer is hosted within the MMC.

Figure 1. Microsoft Transaction Server Explorer

Performance

MTS implements scalability services that enable these “single-user” components to scale to support a large number of concurrent users. One of these services is a thread pool. MTS creates and runs multiple component instances -- one for each caller -- within a single process. This enables single-user components to execute in a multiuser environment without requiring a process-per-user architecture. The diagram below illustrates this architecture.

Figure 2. Process per single user (left) and MTS single user components in a multiuser environment (right)

MTS multiplexes connections to system resources. Often in a distributed application, the system cost of establishing a connection to a back-end resource like a database far exceeds the system cost of the work involved in, for example, updating a record. To overcome this problem, MTS pools connections to back-end resources like databases.

Applications based on distributed objects can encounter scalability problems due to difficulties involved with state management. For example, in a sales management application built with distributed objects with 500 users, there may be 500 concurrent requests from client systems to create a separate instance of a customer object. 500 instances of a customer object that maintains state on a server may cause a server to stall.

MTS provides two related component state management scalability services: Just-in-Time (JIT) Activation and As Soon as Possible (ASAP) Deactivation. Here's how they work: clients create components inside of MTS, get a reference to the component, and start working with it. When a particular set of work is done -- for example, updating a customer record in a sales management system -- a developer makes a call to MTS that deactivates the component. This releases component state, serves as a transaction boundary, and enables server applications to scale. The client still maintains a reference to the server component, so the next time it requests work, MTS uses JIT Activation to fire up a new instance of component. When Microsoft shipped MTS a year ago, it described this capability to activate and deactivate server components as a stateless programming model. A more accurate description is a state-precise programming model, because MTS enables developers to precisely manage the amount of state stored in server-side components.

Packaging, Cross-Platform Interoperability, and Deployment

MTS includes a facility for configuring DLL components into "application packages" that serve as units of execution and deployment. Packages run in a single system process and serve as a security boundary. Developers can deploy all of their application components into a single package, and partition the application so that half of the components run in one package and half of the components run in another package. These two packages can run on separate servers without requiring developers to change how the components are implemented. Microsoft calls this capability to partition an application across multiple back-end servers static load balancing.

MTS enables middle-tier components running on Windows NT® Server to integrate with both clients on resource managers that run on legacy platforms. Windows and UNIX clients can call MTS components via DCOM. MTS does not require the installation of client-side libraries and has no client footprint. While MTS itself doesn't have a footprint, it may be called by COM components installed on a client machine, using Distributed Component Object Model (DCOM) as the network protocol (you can use the MTS Explorer to automatically install the client side of MTS applications).

MTS uses industry-standard transaction processing protocols to integrate with databases and TP Monitors running on legacy UNIX and mainframe systems. MTS includes native transaction support for Microsoft® SQL Server™ and Microsoft® Message Queue Server using OLE Transactions, a distributed object-based transaction protocol. MTS implements the Open Groups XA transaction protocol, enabling middle-tier COM applications to work with Oracle, IBM DB2, Informix, and Sybase databases on Windows NT, UNIX, and other platforms. MTS 2.0 also ships with a transactional ODBC driver for Oracle’s driver.

Microsoft® SNA Server 4.0 includes COM Transaction Integrator (COMTI), formerly known as “Cedar,” a technology that enables MTS applications to enlist in mainframe CICS and IMS applications using IBM’s SNA LU 6.2 Sync Level 2. Thus developers can build new, transacted applications using components that integrate with legacy mainframe applications.

MTS's ability to integrate through transactions Oracle on UNIX, CICS on mainframes, and SQL Server on Windows NT servers creates an application integration environment for middle-tier components that enables companies to leverage investments in existing systems.

The MTS application environment includes the Transaction Server Explorer, a Microsoft Management Console (MMC) snap-in that provides developers and administrators with a Graphical User Interface (GUI) management environment. The Explorer enables developers to install components into MTS, configure transaction and security support, deploy MTS applications across clients and server, and monitor transaction throughput and performance.

Using IIS and MTS to Create a Transaction for a Web Page

In addition to DCOM clients, MTS supports HTML clients through Active Server Pages (since the release of IIS 3.0). An ASP script can invoke a component running inside of MTS, even components that are also being invoked through DCOM clients. Supporting both “thin” HTML clients and “smart” DCOM clients from a common component-based middle-tier application that is integrated with legacy systems makes IIS with MTS a powerful application platform for Web applications.

And this powerful platform just got better with IIS 4.0. In IIS 4.0, ASP uses MTS 2.0 as its application engine. The two technologies ship together in the Windows NT 4.0 Option Pack, use a common thread pool, and both use Microsoft Management Console (MMC) as a common management console.

Figure 3. Microsoft Management Console

IIS 4.0 ASP scripts run as MTS applications. Developers can configure their Web applications to run out-of-process, enabling the application to execute in its own MTS package. This provides crash protection for ASP applications.

The integration of IIS and MTS enables developers to configure their Web pages to execute within an MTS transaction. The ASP script below shows how:

<% TRANSACTION=REQUIRED %>
<%
...
Set objAd = Server.CreateObject("bus_Ad.Ad")
objAd.PlaceAd(…)
…
Sub OnTransactionCommit()
  Response.Write “Ad has been placed”
End Sub
Sub OnTransactionAbort()
  Response.Write “Error has occurred”
End Sub
%>

The <% TRANSACTION = REQUIRED%> tag in an Active Server Page (.asp file) enables MTS to create a transaction for the page. All components invoked within the page (and subsequent component and database calls that flow from these components) can execute within a single MTS transaction, with full rollback and recovery.

IIS 4.0 transactional Web pages include transactional events that developers can use to fork application flow based on transaction outcome. The above example displays different text strings based on transaction outcome. This is a simple, powerful tool for developers building business applications for the Web.

Web Server as Application Platform

As developers deploy the next generation of Web applications, they face the dilemma of integrating production applications with Web technology. The integration of MTS and IIS turns the Web server into an application platform with integrated, enterprise technology for building great applications on the Web. For additional information on MTS, please visit the MTS Web site.

Author James Utzschneider is the lead program manager for the Microsoft Transaction Server team.


Integrating Java with MDAC

Universal Data Access (UDA) provides access to information across the enterprise, including relational and non-relational databases. Its programming interface is tool- and language-independent. These technologies enable integration of diverse data sources and creation of solutions that are easily maintained.

UDA does not require moving data into a single data store, nor does it require commitment to a particular product. UDA is based on open industry specifications with broad industry support, and works with all major established database platforms.

The Microsoft Data Access Components enable Universal Data Access. These components include the following:

Microsoft® ActiveX® Data Objects (ADO)

A provider-neutral, application-level data access object model that can be used from Java and other languages. ADO extensions include a set of Java classes that extend the ADO interfaces and create notifications interesting to the Java programmer. These extensions expose functions that return Java types. In addition, the extensions are optimized to improve performance. The Java class directly accesses the native data types in the OLE DB Rowset object and returns them to the Java programmer as Java types without first converting them to and from a Variant.

Remote Data Service (RDS)

Formerly known as the Advanced Data Connector (ADC); Microsoft's programming model for distributed data access through the Web. RDS is a feature of ADO that makes full-featured, datacentric Web applications a reality by combining data manipulation of retrieved data, efficient client-side caching, and support for data-aware Microsoft® ActiveX® Controls with an elegant and powerful programming model.

OLE DB

Microsoft's specification for a set of data access interfaces that enable data stores of all types and sizes to work seamlessly together. These interfaces are accessible from Java. There are also Java compiler directives that specify a function will be mapped as an OLE-compatible function.

Open Database Connectivity (ODBC)

A C-level API for Microsoft® SQL Server™ data sources and provides a common interface for accessing heterogeneous SQL databases. ODBC is based on SQL as a standard for accessing data. ODBC's interface provides maximum interoperability: A single application can access different database management systems (DBMSs) through a common set of code. This enables a developer to build and distribute a client/server application without targeting a specific DBMS or having to know specific details of various back-end data sources. Java developers can code directly to the ODBC API by declaring various functions and then using them to connect, send SQL statements, retrieve results, get errors, disconnect, and so on. However, it is simpler to use ADO, Remote Data Objects (RDO), and Data Access Objects (DAO) to access ODBC data from applications.

This section provides the following MDAC documents.

White Papers

Microsoft Strategy for Universal Data Access provides an in-depth look at Microsoft's strategy for providing access to information across an organization, from the desktop to the enterprise.

Implementing ADO with Various Development Languages describes how to implement ADO applications using Microsoft® Visual Basic® for Applications (VBA); Microsoft® Visual C++® (Visual C++); Java; Microsoft® Visual Basic®, Scripting Edition (VBScript), and Microsoft® JScript™.

ADO for Windows Foundation Classes Overview provides an overview of ActiveX® Data Objects for Windows Foundation Classes (ADO/WFC) and demonstrates how to use its components.

Windows Foundation Classes for Java and ADO/WFC explains and clarifies the usage of ADO/WFC. It provides a number of Java and WFC examples that illustrate not only how to use ADO from Visual J++, but why you would want to use ADO.

ADO/WFC vs. JDBC compares the Java JDBC interface to SQL data versus ADO/WFC and also versus the OLE DB–based data solution that ships with Visual J++ and WFC.

OLE DB/ADO: Making Universal Data Access a Reality reviews the Universal Data Access platform, presents an overview of ADO, and describes a service component architecture that enables you to compose services transparently to consumers and providers.

Tutorials

ADO Tutorial shows how to use the ADO programming model to query and update a data source.

RDS Tutorial shows how to use the RDS programming model to query and update a data source.

Package Reference

AdoEnums (ADO/WFC Syntax) documents the ADO enumerations in WFC classes.

Syntax Index (ADO/WFC) (com.ms.wfc.data package). The ADO Language Reference uses the Visual Basic programming language to illustrate ADO method and property syntax. This index is a cross-reference to the ADO Language Reference topics based on Visual J++ and ADO for Windows Foundation Classes (ADO/WFC).


AdoEnums (ADO/WFC Syntax)

To use AdoEnums, specify the fully qualified name of the enumerated constant. For example, the first constant in the AdoEnums.Schema class is AdoEnums.Schema.PROVIDERSPECIFIC.

Each topic lists the values of the constants. However, use the constant names, not the values. The values are subject to change between releases.

The tables that follow contain three headings that pertain to the core ADO documentation.

  • Topic This heading is a link to the ADO documentation topic where the enumerated constant is described.

  • Prefix The names of the ADO/WFC enumerated constants are derived from their ADO counterparts. However, when you read the ADO documentation, it may not be obvious which is the corresponding ADO constant.

    The last qualifier in the ADO/WFC constant is an identifier, written in upper-case. The ADO constant consists of a prefix, followed by the identifier written in mixed-case.

    For example, the equivalent of the ADO/WFC constant, AdoEnums.Schema.PROVIDERSPECIFIC, is the ADO constant, adSchemaProviderSpecific. In this case the prefix, adSchema, is prepended to the identifier, ProviderSpecific.

    This heading lists the prefix you should append to the ADO/WFC identifier to generate the name of the ADO enumerated constant.

  • Declared ADO COM interface files declare each set of enumerated constants. This heading shows the name of that declaration.

    The name of the COM declaration can be derived from the ADO/WFC constant by suffixing Enum to the second qualifier. For example, the COM declaration associated with AdoEnums.Schema.* is named SchemaEnum.

The following enumerations are defined in ADO/WFC.

AdcPropAsyncThreadPriority | DataType | ObjectState
AdcPropUpdateCriteria | EditMode | ParameterAttributes
Affect | ErrorValue | ParameterDirection
Bookmark | EventReason | PersistFormat
CommandType | EventStatus | Position
Compare | ExecuteOption | PropertyAttributes
ConnectMode | FieldAttribute | RecordStatus
ConnectOption | FilterGroup | Resync
ConnectPrompt | GetRowsOption | Schema
CursorLocation | IsolationLevel | SearchDirection
CursorOption | LockType | StringFormat
CursorType | MarshalOptions | XactAttribute


AdoEnums.AdcPropAsyncThreadPriority.*

package com.ms.wfc.data

Topic For an RDS Recordset object, specifies the execution priority of the asynchronous thread that fetches data. Use these constants with the recordset "Background Thread Priority" dynamic property.

Prefix adPriority

Declared ADCPROP_ASYNCTHREADPRIORITY_ENUM

Constant | Value
LOWEST | 1
BELOWNORMAL | 2
NORMAL | 3
ABOVENORMAL | 4
HIGHEST | 5


AdoEnums.AdcPropUpdateCriteria.*

package com.ms.wfc.data

Topic Specifies which fields can be used to detect conflicts during an optimistic update of a row of the data source with an RDS Recordset object. The constants specify that collisions should be detected if there is a change to the key field (that is, the data source row has been deleted); a change to any column in the data source row; a change to the same column of the data source row as the updated column of the recordset; or if the data source row has merely been accessed. Use these constants with the recordset "Update Criteria" dynamic property.

Prefix adCriteria

Declared ADCPROP_UPDATECRITERIA_ENUM

Constant | Value
KEY | 0
ALLCOLS | 1
UPDCOLS | 2
TIMESTAMP | 3


AdoEnums.Affect.*

package com.ms.wfc.data

Topic UpdateBatch Method

Prefix adAffect

Declared AffectEnum

Constant | Value
CURRENT | 1
GROUP | 2
ALL | 3
ALLCHAPTERS | 4


AdoEnums.Bookmark.*

package com.ms.wfc.data

Topic Find, GetRows, Move Methods

Prefix adBookmark

Declared BookmarkEnum

Constant | Value
CURRENT | 0
FIRST | 1
LAST | 2


AdoEnums.CommandType.*

package com.ms.wfc.data

Topic CommandType Property

Prefix adCmd

Declared CommandTypeEnum

Constant | Value
UNSPECIFIED | -1
UNKNOWN | 8
TEXT | 1
TABLE | 2
STOREDPROC | 4
FILE | 256
TABLEDIRECT | 512


AdoEnums.Compare.*

package com.ms.wfc.data

Topic CompareBookmarks Method

Prefix adCompare

Declared CompareEnum

Constant | Value
LESSTHAN | 0
EQUAL | 1
GREATERTHAN | 2
NOTEQUAL | 3
NOTCOMPARABLE | 4


AdoEnums.ConnectMode.*

package com.ms.wfc.data

Topic Mode Property

Prefix adMode

Declared ConnectModeEnum

Constant | Value
UNKNOWN | 0
READ | 1
WRITE | 2
READWRITE | 3
SHAREDENYREAD | 4
SHAREDENYWRITE | 8
SHAREEXCLUSIVE | 12
SHAREDENYNONE | 16


AdoEnums.ConnectOption.*

package com.ms.wfc.data

Topic Connect.Open Method (referred to as OpenOptionEnum)

Prefix ad

Declared ConnectOptionEnum

Constant | Value
CONNECTUNSPECIFIED | -1
ASYNCCONNECT | 16


AdoEnums.ConnectPrompt.*

package com.ms.wfc.data

Topic Specifies whether a dialog box should be displayed to prompt for missing parameters when opening a connection to an ODBC data source. The constants specify whether to always prompt, prompt if more information is needed, prompt if more information is needed but optional parameters are not allowed, or never prompt. Use these constants with the connection "Prompt" dynamic property.

Prefix adPrompt

Declared ConnectPromptEnum

Constant | Value
ALWAYS | 1
COMPLETE | 2
COMPLETEREQUIRED | 3
NEVER | 4


AdoEnums.CursorLocation.*

package com.ms.wfc.data

Topic CursorLocation Property

Prefix adUse

Declared CursorLocationEnum

Constant | Value
NONE | 1
SERVER | 2
CLIENT | 3


AdoEnums.CursorOption.*

package com.ms.wfc.data

Topic Supports Method

Prefix ad

Declared CursorOptionEnum

Constant | Value
HOLDRECORDS | 256
MOVEPREVIOUS | 512
ADDNEW | 16778240
DELETE | 16779264
UPDATE | 16809984
BOOKMARK | 8192
APPROXPOSITION | 16384
UPDATEBATCH | 65536
RESYNC | 131072
NOTIFY | 262144
FIND | 524288


AdoEnums.CursorType.*

package com.ms.wfc.data

Topic CursorType Property

Prefix adOpen

Declared CursorTypeEnum

Constant | Value
UNSPECIFIED | -1
FORWARDONLY | 0
KEYSET | 1
DYNAMIC | 2
STATIC | 3


AdoEnums.DataType.*

package com.ms.wfc.data

Topic Append Method

Prefix ad

Declared DataTypeEnum

Constant | Value | Constant | Value | Constant | Value
EMPTY | 0 | DECIMAL | 14 | DBTIMESTAMP | 135
TINYINT | 16 | NUMERIC | 131 | BSTR | 8
SMALLINT | 2 | BOOLEAN | 11 | CHAR | 129
INTEGER | 3 | ERROR | 10 | VARCHAR | 200
BIGINT | 20 | USERDEFINED | 132 | LONGVARCHAR | 201
UNSIGNEDTINYINT | 17 | VARIANT | 12 | WCHAR | 130
UNSIGNEDSMALLINT | 18 | IDISPATCH | 9 | VARWCHAR | 202
UNSIGNEDINT | 19 | IUNKNOWN | 13 | LONGVARWCHAR | 203
UNSIGNEDBIGINT | 21 | GUID | 72 | BINARY | 128
SINGLE | 4 | DATE | 7 | VARBINARY | 204
DOUBLE | 5 | DBDATE | 133 | LONGVARBINARY | 205
CURRENCY | 6 | DBTIME | 134 | CHAPTER | 136


AdoEnums.EditMode.*

package com.ms.wfc.data

Topic EditMode Property

Prefix adEdit

Declared EditModeEnum

Constant | Value
NONE | 0
INPROGRESS | 1
ADD | 2
DELETE | 4


AdoEnums.ErrorValue.*

package com.ms.wfc.data

Topic ADO Error Codes

Prefix adErr

Declared ErrorValueEnum

Constant | Value | Constant | Value
INVALIDARGUMENT | 3001 | OBJECTOPEN | 3705
NOCURRENTRECORD | 3021 | PROVIDERNOTFOUND | 3706
ILLEGALOPERATION | 3219 | BOUNDTOCOMMAND | 3707
INTRANSACTION | 3246 | INVALIDPARAMINFO | 3708
FEATURENOTAVAILABLE | 3251 | INVALIDCONNECTION | 3709
ITEMNOTFOUND | 3265 | NOTREENTRANT | 3710
OBJECTINCOLLECTION | 3367 | STILLEXECUTING | 3711
OBJECTNOTSET | 3420 | OPERATIONCANCELLED | 3712
DATACONVERSION | 3421 | STILLCONNECTING | 3713
OBJECTCLOSED | 3704 | NOTEXECUTING | 3715
 |  | UNSAFEOPERATION | 3716


AdoEnums.EventReason.*

package com.ms.wfc.data

Topic WillMove, WillChangeRecord, WillChangeRecordset Methods

Prefix adRsn

Declared EventReasonEnum

Constant | Value | Constant | Value
ADDNEW | 1 | CLOSE | 9
DELETE | 2 | MOVE | 10
UPDATE | 3 | FIRSTCHANGE | 11
UNDOUPDATE | 4 | MOVEFIRST | 12
UNDOADDNEW | 5 | MOVENEXT | 13
UNDODELETE | 6 | MOVEPREVIOUS | 14
REQUERY | 7 | MOVELAST | 15
RESYNCH | 8 |  |


AdoEnums.EventStatus.*

package com.ms.wfc.data

Topic All ADO Events

Prefix adStatus

Declared EventStatusEnum

Constant | Value
OK | 1
ERRORSOCCURRED | 2
CANTDENY | 3
CANCEL | 4
UNWANTEDEVENT | 5


AdoEnums.ExecuteOption.*

package com.ms.wfc.data

Topic Execute Method

Prefix ad

Declared ExecuteOptionEnum

Constant | Value
UNSPECIFIED | -1
ASYNCEXECUTE | 16
ASYNCFETCH | 32
ASYNCFETCHNONBLOCKING | 64


AdoEnums.FieldAttribute.*

package com.ms.wfc.data

Topic Attribute Property

Prefix adFld

Declared FieldAttributeEnum

Constant | Value
UNSPECIFIED | -1
MAYDEFER | 2
UPDATABLE | 4
UNKNOWNUPDATABLE | 8
FIXED | 16
ISNULLABLE | 32
MAYBENULL | 64
LONG | 128
ROWID | 256
ROWVERSION | 512
CACHEDEFERRED | 4096


AdoEnums.FilterGroup.*

package com.ms.wfc.data

Topic Filter Property

Prefix adFilter

Declared FilterGroupEnum

Constant | Value
NONE | 0
PENDINGRECORDS | 1
AFFECTEDRECORDS | 2
FETCHEDRECORDS | 3
PREDICATE | 4
CONFLICTINGRECORDS | 5


AdoEnums.GetRowsOption.*

package com.ms.wfc.data

Topic GetRows Method

Prefix adGetRows

Declared GetRowsOptionEnum

Constant | Value
REST | -1


AdoEnums.IsolationLevel.*

package com.ms.wfc.data

Topic IsolationLevel Property

Prefix adXact

Declared IsolationLevelEnum

Constant | Value
UNSPECIFIED | -1
CHAOS | 16
READUNCOMMITTED | 256
BROWSE | 256
CURSORSTABILITY | 4096
READCOMMITTED | 4096
REPEATABLEREAD | 65536
SERIALIZABLE | 1048576
ISOLATED | 1048576


AdoEnums.LockType.*

package com.ms.wfc.data

Topic LockType Property

Prefix adLock

Declared LockTypeEnum

Constant | Value
UNSPECIFIED | -1
READONLY | 1
PESSIMISTIC | 2
OPTIMISTIC | 3
BATCHOPTIMISTIC | 4


AdoEnums.MarshalOptions.*

package com.ms.wfc.data

Topic MarshalOptions Property

Prefix adMarshal

Declared MarshalOptionsEnum

Constant | Value
ALL | 0
MODIFIEDONLY | 1


AdoEnums.ObjectState.*

package com.ms.wfc.data

Topic State Property

Prefix adState

Declared ObjectStateEnum

Constant | Value
CLOSED | 0
OPEN | 1
CONNECTING | 2
EXECUTING | 4
FETCHING | 8


AdoEnums.ParameterAttributes.*

package com.ms.wfc.data

Topic Attribute Property

Prefix adParam

Declared ParameterAttributesEnum

Constant | Value
SIGNED | 16
NULLABLE | 64
LONG | 128


AdoEnums.ParameterDirection.*

package com.ms.wfc.data

Topic Direction Property

Prefix adParam

Declared ParameterDirectionEnum

Constant | Value
UNKNOWN | 0
INPUT | 1
OUTPUT | 2
INPUTOUTPUT | 3
RETURNVALUE | 4


AdoEnums.PersistFormat.*

package com.ms.wfc.data

Topic Save Method

Prefix adPersist

Declared PersistFormatEnum

Constant | Value
ADTG | 0
XML | 1


AdoEnums.Position.*

package com.ms.wfc.data

Topic AbsolutePage and AbsolutePosition Properties

Prefix adPos

Declared PositionEnum

Constant | Value
UNKNOWN | -1
BOF | -2
EOF | -3


AdoEnums.PropertyAttributes.*

package com.ms.wfc.data

Topic Attribute Property

Prefix adProp

Declared PropertyAttributesEnum

Constant | Value
NOTSUPPORTED | 0
REQUIRED | 1
OPTIONAL | 2
READ | 512
WRITE | 1024


AdoEnums.RecordStatus.*

package com.ms.wfc.data

Topic Status Property

Prefix adRec

Declared RecordStatusEnum

Constant | Value | Constant | Value
OK | 0 | CANTRELEASE | 1024
NEW | 1 | CONCURRENCYVIOLATION | 2048
MODIFIED | 2 | INTEGRITYVIOLATION | 4096
DELETED | 4 | MAXCHANGESEXCEEDED | 8192
UNMODIFIED | 8 | OBJECTOPEN | 16384
INVALID | 16 | OUTOFMEMORY | 32768
MULTIPLECHANGES | 64 | PERMISSIONDENIED | 65536
PENDINGCHANGES | 128 | SCHEMAVIOLATION | 131072
CANCELED | 256 | DBDELETED | 262144


AdoEnums.Resync.*

package com.ms.wfc.data

Topic Resync Method

Prefix adResync

Declared ResyncEnum

Constant | Value
UNDERLYINGVALUES | 1
ALLVALUES | 2


AdoEnums.Schema.*

package com.ms.wfc.data

Topic OpenSchema Method

Prefix adSchema

Declared SchemaEnum

Constant | Value | Constant | Value
PROVIDERSPECIFIC | -1 | STATISTICS | 19
ASSERTS | 0 | TABLES | 20
CATALOGS | 1 | TRANSLATIONS | 21
CHARACTERSETS | 2 | PROVIDERTYPES | 22
COLLATIONS | 3 | VIEWS | 23
COLUMNS | 4 | VIEWCOLUMNUSAGE | 24
CHECKCONSTRAINTS | 5 | VIEWTABLEUSAGE | 25
CONSTRAINTCOLUMNUSAGE | 6 | PROCEDUREPARAMETERS | 26
CONSTRAINTTABLEUSAGE | 7 | FOREIGNKEYS | 27
KEYCOLUMNUSAGE | 8 | PRIMARYKEYS | 28
REFERENTIALCONTRAINTS | 9 | PROCEDURECOLUMNS | 29
TABLECONSTRAINTS | 10 | DBINFOKEYWORDS | 30
COLUMNSDOMAINUSAGE | 11 | DBINFOLITERALS | 31
INDEXES | 12 | CUBES | 32
COLUMNPRIVILEGES | 13 | DIMENSIONS | 33
TABLEPRIVILEGES | 14 | HIERARCHIES | 34
USAGEPRIVILEGES | 15 | LEVELS | 35
PROCEDURES | 16 | MEASURES | 36
SCHEMATA | 17 | PROPERTIES | 37
SQLLANGUAGES | 18 | MEMBERS | 38


AdoEnums.SearchDirection.*

package com.ms.wfc.data

Topic Find Method

Prefix adSearch

Declared SearchDirectionEnum

Constant | Value
FORWARD | 1
BACKWARD | -1


AdoEnums.StringFormat.*

package com.ms.wfc.data

Topic GetString Method

Prefix ad

Declared StringFormatEnum

Constant | Value
CLIPSTRING | 2


AdoEnums.XactAttribute.*

package com.ms.wfc.data

Topic Attribute Property

Prefix adXact

Declared XactAttributeEnum

Constant | Value
COMMITRETAINING | 131072
ABORTRETAINING | 262144
ASYNCPHASEONE | 524288
SYNCPHASEONE | 1048576


com.ms.wfc.data

Syntax Index (ADO/WFC)

The ADO Language Reference uses the Microsoft® Visual Basic® programming language to illustrate ADO method and property syntax. This index is a cross-reference to the ADO Language Reference topics based on Microsoft® Visual J++™ and ADO for Windows Foundation Classes (ADO/WFC). When differences in syntax arise, use the function signatures in this index as opposed to the syntax listings in the language reference topic.

Method and property syntax are listed for the following elements:

ActiveX Data Objects

  • Collections (Parameters, Fields, Errors)

  • Command object

  • Connection object

  • Error object

  • Field object

  • Parameter object

  • Recordset object

Remote Data Service

  • DataSpace

  • ObjectProxy


Connection (ADO/WFC Syntax)

package com.ms.wfc.data

Constructor

public Connection()
public Connection(String connectionstring)

Methods

public int beginTrans()
public void commitTrans()
public void rollbackTrans()

public void cancel()

public void close()

public com.ms.wfc.data.Recordset execute(String commandText)
public com.ms.wfc.data.Recordset execute(String commandText, int options)

public int executeUpdate(String commandText)
public int executeUpdate(String commandText, int options)

public void open()
public void open(String connectionString)
public void open(String connectionString, String userID)
public void open(String connectionString, String userID, String password)
public void open(String connectionString, String userID, String password, int options)

public Recordset openSchema(int schema, Object[] restrictions, String schemaID)
public Recordset openSchema(int schema)
public Recordset openSchema(int schema, Object[] restrictions)

Properties

public int getAttributes()
public void setAttributes(int attr)

public int getCommandTimeout()
public void setCommandTimeout(int timeout)

public String getConnectionString()
public void setConnectionString(String con)

public int getConnectionTimeout()
public void setConnectionTimeout(int timeout)

public int getCursorLocation()
public void setCursorLocation(int cursorLoc)

public String getDefaultDatabase()
public void setDefaultDatabase(String db)

public int getIsolationLevel()
public void setIsolationLevel(int level)

public int getMode()
public void setMode(int mode)

public String getProvider()
public void setProvider(String provider)

public int getState()

public String getVersion()

public AdoProperties getProperties()

public com.ms.wfc.data.Errors getErrors()

Events

For more information about ADO/WFC events, see ADO Events in ADO/WFC.

public void addOnBeginTransComplete(ConnectionEventHandler handler)
public void removeOnBeginTransComplete(ConnectionEventHandler handler)

public void addOnCommitTransComplete(ConnectionEventHandler handler)
public void removeOnCommitTransComplete(ConnectionEventHandler handler)

public void addOnConnectComplete(ConnectionEventHandler handler)
public void removeOnConnectComplete(ConnectionEventHandler handler)

public void addOnDisconnect(ConnectionEventHandler handler)
public void removeOnDisconnect(ConnectionEventHandler handler)

public void addOnExecuteComplete(ConnectionEventHandler handler)
public void removeOnExecuteComplete(ConnectionEventHandler handler)

public void addOnInfoMessage(ConnectionEventHandler handler)
public void removeOnInfoMessage(ConnectionEventHandler handler)

public void addOnRollbackTransComplete(ConnectionEventHandler handler)
public void removeOnRollbackTransComplete(ConnectionEventHandler handler)

public void addOnWillConnect(ConnectionEventHandler handler)
public void removeOnWillConnect(ConnectionEventHandler handler)

public void addOnWillExecute(ConnectionEventHandler handler)
public void removeOnWillExecute(ConnectionEventHandler handler)


Command (ADO/WFC Syntax)

package com.ms.wfc.data

Constructor

public Command()
public Command(String commandtext)

Methods

public void cancel()

public com.ms.wfc.data.Parameter createParameter(String Name, int Type, int Direction, int Size, Object Value)

public Recordset execute()
public Recordset execute(Object[] parameters)
public Recordset execute(Object[] parameters, int options)

public int executeUpdate(Object[] parameters)
public int executeUpdate(Object[] parameters, int options)
public int executeUpdate()

Properties

public com.ms.wfc.data.Connection getActiveConnection()
public void setActiveConnection(com.ms.wfc.data.Connection con)
public void setActiveConnection(String conString)

public String getCommandText()
public void setCommandText(String command)

public int getCommandTimeout()
public void setCommandTimeout(int timeout)

public int getCommandType()
public void setCommandType(int type)

public String getName()
public void setName(String name)

public boolean getPrepared()
public void setPrepared(boolean prepared)

public int getState()

public com.ms.wfc.data.Parameter getParameter(int n)
public com.ms.wfc.data.Parameter getParameter(String n)

public com.ms.wfc.data.Parameters getParameters()

public AdoProperties getProperties()


Parameter (ADO/WFC Syntax)

package com.ms.wfc.data

Constructor

public Parameter()
public Parameter(String name)
public Parameter(String name, int type)
public Parameter(String name, int type, int dir)
public Parameter(String name, int type, int dir, int size)
public Parameter(String name, int type, int dir, int size, Object value)

Methods

public void appendChunk(byte[] bytes)
public void appendChunk(char[] chars)
public void appendChunk(String chars)

Properties

public int getAttributes()
public void setAttributes(int attr)

public int getDirection()
public void setDirection(int dir)

public String getName()
public void setName(String name)

public int getNumericScale()
public void setNumericScale(int scale)

public int getPrecision()
public void setPrecision(int prec)

public int getSize()
public void setSize(int size)

public int getType()
public void setType(int type)

public com.ms.com.Variant getValue()
public void setValue(Object v)

public AdoProperties getProperties()

Parameter Accessor Methods

The Value property of a Parameter object gets or sets the content of that object. The content is represented as a VARIANT, a type of object that can be assigned a value and any of several data types.

ADO/WFC implements the Value property with the getValue method, which returns a VARIANT object; and the setValue method, which takes a VARIANT as an argument. VARIANTs are highly efficient in certain languages, such as Microsoft Visual Basic. However, you can attain better performance in Microsoft Visual J++ by using native Java data types.

In addition to the Value property, ADO/WFC provides accessor methods that use Java data types to get and set the content of Parameter objects. Most of these methods have names of the form get DataType or set DataType.

There is one noteworthy exception: There is no getNull property; instead, there is an isNull property that returns a Boolean value indicating whether the field is null.

public boolean getBoolean()
public void setBoolean(boolean v)

public byte getByte()
public void setByte(byte v)

public double getDouble()
public void setDouble(double v)

public float getFloat()
public void setFloat(float v)

public int getInt()
public void setInt(int v)

public long getLong()
public void setLong(long v)

public short getShort()
public void setShort(short v)

public String getString()
public void setString(String v)

public boolean isNull()
public void setNull()


Recordset (ADO/WFC Syntax)

package com.ms.wfc.data

Constructors

public Recordset()
public Recordset(Object r)

Methods

public void addNew(Object[] fieldList, Object[] valueList)
public void addNew(Object[] valueList)
public void addNew()

public void cancel()

public void cancelBatch(int affectRecords)
public void cancelBatch()

public void cancelUpdate()

public Object clone()
public Object clone(int lockType)

public void close()

public int compareBookmarks(Object bookmark1, Object bookmark2)

public void delete(int affectRecords)
public void delete()

public void find(String criteria)
public void find(String criteria, int SkipRecords)
public void find(String criteria, int SkipRecords, int searchDirection)
public void find(String criteria, int SkipRecords, int searchDirection, Object bmkStart)

public Object[][] getRows(int Rows, Object bmkStart, Object[] fieldList)

public void move(int numRecords)
public void move(int numRecords, Object bmkStart)

public void moveFirst()
public void moveLast()
public void moveNext()
public void movePrevious()

public Recordset nextRecordset()
public Recordset nextRecordset(int[] recordsAffected)

public void open()
public void open(Object source)
public void open(Object source, Object activeConnection)
public void open(Object source, Object activeConnection, int cursorType)
public void open(Object source, Object activeConnection, int cursorType,
int lockType)
public void open(Object source, Object activeConnection, int cursorType,
int lockType, int options)

public void requery()
public void requery(int options)

public void resync()
public void resync(int affectRecords, int resyncValues)

public void save(String fileName)
public void save(String fileName, int persistFormat)

public boolean supports(int cursorOptions)

public void update()
public void update(Object[] valueList)
public void update(Object[] fieldList, Object[] valueList)

public void updateBatch()
public void updateBatch(int affectRecords)

Properties

public int getAbsolutePage()
public void setAbsolutePage(int page)

public int getAbsolutePosition()
public void setAbsolutePosition(int pos)

public Command getActiveCommand()

public Connection getActiveConnection()
public void setActiveConnection(String conn)
public void setActiveConnection(com.ms.wfc.data.Connection c)

public boolean getBOF()

public boolean getEOF()

public Object getBookmark()
public void setBookmark(Object bmk)

public int getCacheSize()
public void setCacheSize(int size)

public int getCursorLocation()
public void setCursorLocation(int cursorLoc)

public int getCursorType()
public void setCursorType(int cursorType)

public String getDataMember()
public void setDataMember(String pbstrDataMember)

public Iunknown getDataSource()
public void setDataSource(IUnknown dataSource)

public int getEditMode()

public Object getFilter()
public void setFilter(Object filter)

public int getLockType()
public void setLockType(int lockType)

public int getMarshalOptions()
public void setMarshalOptions(int options)

public int getMaxRecords()
public void setMaxRecords(int maxRecords)

public int getPageCount()

public int getPageSize()
public void setPageSize(int pageSize)

public int getRecordCount()

public String getSort()
public void setSort(String criteria)

public String getSource()
public void setSource(String query)
public void setSource(com.ms.wfc.data.Command command)

public int getState()

public int getStatus()

public boolean getStayInSync()
public void setStayInSync(boolean pbStayInSync)

public com.ms.wfc.data.Field getField(int n)
public com.ms.wfc.data.Field getField(String n)

public com.ms.wfc.data.Fields getFields()

public AdoProperties getProperties()

Events

For more information about ADO/WFC events, see ADO Events in ADO/WFC.

public void addOnEndOfRecordset(RecordsetEventHandler handler)
public void removeOnEndOfRecordset(RecordsetEventHandler handler)

public void addOnFetchComplete(RecordsetEventHandler handler)
public void removeOnFetchComplete(RecordsetEventHandler handler)

public void addOnFetchProgress(RecordsetEventHandler handler)
public void removeOnFetchProgress(RecordsetEventHandler handler)

public void addOnFieldChangeComplete(RecordsetEventHandler handler)
public void removeOnFieldChangeComplete(RecordsetEventHandler handler)

public void addOnMoveComplete(RecordsetEventHandler handler)
public void removeOnMoveComplete(RecordsetEventHandler handler)

public void addOnRecordChangeComplete(RecordsetEventHandler handler)
public void removeOnRecordChangeComplete(RecordsetEventHandler handler)

public void addOnRecordsetChangeComplete(RecordsetEventHandler handler)
public void removeOnRecordsetChangeComplete(RecordsetEventHandler handler)

public void addOnWillChangeField(RecordsetEventHandler handler)
public void removeOnWillChangeField(RecordsetEventHandler handler)

public void addOnWillChangeRecord(RecordsetEventHandler handler)
public void removeOnWillChangeRecord(RecordsetEventHandler handler)

public void addOnWillChangeRecordset(RecordsetEventHandler handler)
public void removeOnWillChangeRecordset(RecordsetEventHandler handler)

public void addOnWillMove(RecordsetEventHandler handler)
public void removeOnWillMove(RecordsetEventHandler handler)


Field (ADO/WFC Syntax)

package com.ms.wfc.data

Methods

public void appendChunk(byte[] bytes)
public void appendChunk(char[] chars)
public void appendChunk(String chars)

public byte[] getByteChunk(int len)
public char[] getCharChunk(int len)
public String getStringChunk(int len)

Properties

public int getActualSize()

public int getAttributes()
public void setAttributes(int pl)

public com.ms.com.IUnknown getDataFormat()
public void setDataFormat(com.ms.com.IUnknown format)
(For more information, see the Microsoft Visual J++ WFC Reference documentation for the com.ms.wfc.data.IDataFormat interface.)

public int getDefinedSize()
public void setDefinedSize(int pl)

public String getName()

public int getNumericScale()
public void setNumericScale(byte pbNumericScale)

public Variant getOriginalValue()

public int getPrecision()
public void setPrecision(byte pbPrecision)

public int getType()
public void setType(int pDataType)

public Variant getUnderlyingValue()

public Variant getValue()
public void setValue(Variant value)

public AdoProperties getProperties()

Field Accessor Methods

The Value property of a Field object gets or sets the content of that object. The content is represented as a VARIANT, a type of object that can be assigned a value and any of several data types.

ADO/WFC implements the Value property with the getValue method, which returns a VARIANT object; and the setValue method, which takes a VARIANT as an argument. VARIANTs are highly efficient in certain languages, such as Microsoft Visual Basic. However, you can attain better performance in Microsoft Visual J++ by using native Java data types.

In addition to the Value property, ADO/WFC provides accessor methods that use Java data types to get and set the content of Field objects. Most of these methods have names of the form get DataType or set DataType.

There are two noteworthy exceptions: One of the getObject methods returns an object coerced to a specified class. There is no getNull property; instead, there is an isNull property that returns a Boolean value indicating whether the field is null.

public native boolean getBoolean();
public void setBoolean(boolean v)

public native byte getByte();
public void setByte(byte v)

public native byte[] getBytes();
public void setBytes(byte[] v)

public native double getDouble();
public void setDouble(double v)

public native float getFloat();
public void setFloat(float v)

public native int getInt();
public void setInt(int v)

public native long getLong();
public void setLong(long v)

public native short getShort();
public void setShort(short v)

public native String getString();
public void setString(String v)

public native boolean isNull();
public void setNull()

public Object getObject()
public Object getObject(Class c)
public void setObject(Object value)


Error (ADO/WFC Syntax)

package com.ms.wfc.data

Properties

public String getDescription()

public int getNativeError()

public int getNumber()

public String getSource()

public String getSQLState()


Collections (ADO/WFC Syntax)

package com.ms.wfc.data

Parameters

Methods

public void append(com.ms.wfc.data.Parameter param)

public void delete(int n)
public void delete(String s)

public void refresh()

public Parameter getItem(int n)
public Parameter getItem(String s)

Properties

public int getCount()

Fields

Methods

public void append(String name, int type)
public void append(String name, int type, int definedSize)
public void append(String name, int type, int definedSize, int attrib)

public void delete(int n)
public void delete(String s)

public void refresh()

public com.ms.wfc.data.Field getItem(int n)
public com.ms.wfc.data.Field getItem(String s)

Properties

public int getCount()

Errors

Methods

public void clear()

public void refresh()

public com.ms.wfc.data.Error getItem(int n)
public com.ms.wfc.data.Error getItem(String s)

Properties

public int getCount()


DataSpace (ADO/WFC Syntax)

The ADO/WFC DataSpace class has one method, createObject, that specifies a server and communication protocol; a server object that processes client application requests, and returns an ObjectProxy object that represents the server.

package com.ms.wfc.data

Constructor

public DataSpace()

Methods

public static ObjectProxy DataSpace.createObject(String progid, String connection)

Properties

public static int getInternetTimeout()
public static void setInternetTimeout(int plInetTimeout)


ObjectProxy (ADO/WFC Syntax)

An ObjectProxy object represents a server, and is returned by the createObject method of the DataSpace object. The ObjectProxy class has one method, call, which can invoke a method on the server and return an object resulting from that invocation.

package com.ms.wfc.data

Methods

Call Method (ADO/WFC Syntax)

Invokes a method on the server represented by the ObjectProxy. Optionally, method arguments may be passed as an array of objects.

Syntax

public Object ObjectProxy.call( String method )
public Object ObjectProxy.call( String method, Object[] args)

Returns

Object An object resulting from invoking the method.

Parameters

ObjectProxy An ObjectProxy object that represents the server.

method A String, containing the name of the method to invoke on the server.

args Optional. An array of objects which are arguments to the method on the server. Java data types are automatically converted to data types suitable for use on the server.


Microsoft Strategy for Universal Data Access

May 1998

By David Lazar

Overview and Scope

This paper provides an in-depth look at Microsoft® Universal Data Access, Microsoft Corporation’s broadly supported strategy for providing access to information across an organization, from the desktop to the enterprise.

The technologies discussed in this paper are multifaceted, spanning many areas of computing, including architecture, programming, networking, and platform integration. The issues are critically important because data and information are at the heart of almost any computer system, and the efficient and effective use of information is what provides business value and strategic advantage. Today, these issues are magnified as organizations begin to broadly implement applications that leverage the Internet and mobile computing.

Access to information is being required in new scenarios, and the complexity of that information continues to grow. Organizations previously had data on the mainframe and in various DBMSs. Now important information is also found in mail stores, file systems, Web-based text and graphical files, and more. Organizations that are able to leverage all of this information, that can expand rather than replace current UNIX and mainframe systems to embrace client/server systems and the Internet, will thrive.

This paper is intended to explain Microsoft strategy for helping organizations achieve maximum business advantage by organizing and accessing their information efficiently. Readers looking for in-depth technical analysis are directed to a companion paper titled “Microsoft Data Access Technologies,” which can be found along with other technical and strategic documents on the Microsoft data access Web site, http://www.microsoft.com/data/.

Customer Requirements for Data Access Technologies

Microsoft has solicited extensive feedback from data access customers on the criteria they use in judging and selecting data access technologies and products. Our research has shown that there are four main criteria used in the decision-making process:

  • High-performance access to data. Simply stated, new data access methods need to provide the same level of performance that is possible through the data provider’s proprietary API. This means that customers don't plan to compromise their number one criterion, performance, for any other benefit. Similarly, services that augment the native capabilities of data providers must be held to the same standards: They must not stand in the way of native performance. And, because applications and components must frequently scale to support hundreds or thousands of concurrently connected users, performance should be maintained as usage grows.

  • Reliability. Customers want their database solutions to perform reliably. They use terms such as “rock-solid” and “fail-safe” to describe their requirements in this area. Underlying these statements is the need to minimize maintenance and support costs, and to reduce the total cost of ownership.

  • Vendor commitment. Customers indicate that they are making strategic commitments to vendors of data access technologies, and are looking for reciprocation. They indicate that database decisions are long term, that they are purchasing not a single release but a string of database and related product releases. On the flip side, customers are very wary of becoming too dependent on a single vendor, a situation they term “vendor lock-in” or “vendor tie-in.” Finally, new technologies should evolve gracefully from current ones, to avoid costly replacement of existing capabilities.

  • Broad industry support. This is defined as market share, as well as the support of vendors of related products and technologies. For customers, broad industry support is a more important gauge than the blessing of a standards body when choosing data access products. Broad industry support carries many benefits — safety in numbers, availability of skilled people to work with the products, and products that work together without expensive integration and customization.

This paper has been structured to explain how the Microsoft Universal Data Access strategy meets these criteria. After the terms and technologies are defined, separate sections address how Microsoft has met or intends to meet each of the criteria.

Definition of the Universal Data Access Strategy

Universal Data Access is a platform, application, and tools initiative that defines and delivers both standards and technologies and is a key element in the Microsoft foundation for application development, the Microsoft® Windows® Distributed interNet Applications (DNA) architecture.

Today, companies building client/server and Web-based database solutions seek maximum business advantage from the data and information distributed throughout their organizations. Universal Data Access provides high-performance access to a variety of data and information sources on multiple platforms and an easy-to-use programming interface that works with practically any tool or language, leveraging the technical skills developers already have. The technologies that support Universal Data Access enable organizations to create easy-to-maintain solutions and use their choice of best-of-breed tools, applications, and data sources on the client, middle tier, or server.

Figure 1

Another benefit of Universal Data Access is that it does not require expensive and time-consuming movement of all corporate data into a single data store, nor does it require commitment to a single vendor’s products. Universal Data Access is based on open industry specifications with broad industry support and works with all major established database products. Universal Data Access is an evolutionary step from standard interfaces such as Open Database Connectivity (ODBC), Remote Data Objects (RDO), and Data Access Objects (DAO), and it significantly extends the functionality of these well-known and well-tested technologies.

A Unified Data Access Model Based on COM

One strength of the Microsoft Universal Data Access strategy is that it is delivered through a common set of modern, object-oriented interfaces. These interfaces are based on the Microsoft Component Object Model (COM), the most widely implemented object technology in the world. COM has become the choice of developers worldwide because it provides the following:

  • The richest integrated services, including transactions, security, message queuing, and data access to support the broadest range of application scenarios

  • The widest choice of tools from multiple vendors using multiple development languages

  • The largest customer base for customizable applications and reusable components

  • Proven interoperability with users’ and developers’ existing investments

Because of the consistency and interoperability afforded through COM, the Microsoft Universal Data Access architecture is open and works with virtually any tool or programming language. It also enables Universal Data Access to provide a consistent data access model at all tiers of the modern application architecture.

Microsoft Universal Data Access architecture exposes COM-based interfaces optimized for both low-level and high-level application development by using OLE DB and ADO, respectively.

Definition of OLE DB

OLE DB is the Microsoft strategic system-level programming interface to data across the organization. OLE DB is an open specification designed to build on the success of ODBC by providing an open standard for accessing all kinds of data. ODBC was created to access relational databases. OLE DB is designed for relational and nonrelational information sources, including: mainframe ISAM/VSAM and hierarchical databases; e-mail and file system stores; text, graphical, and geographical data; custom business objects; and more.

Figure 2

OLE DB defines a collection of COM interfaces that encapsulate various database management system services. These interfaces enable the creation of software components that implement such services. OLE DB components consist of data providers, which contain and expose data; data consumers, which use data; and service components, which process and transport data (such as query processors and cursor engines). OLE DB interfaces are designed to help components integrate smoothly so that OLE DB component vendors can bring high-quality OLE DB components to market quickly. In addition, OLE DB includes a bridge to ODBC to enable continued support for the broad range of ODBC relational database drivers available today.

Definition of ActiveX Data Objects

Microsoft® ActiveX® Data Objects (ADO) is the Microsoft strategic application-level programming interface to data and information. ADO provides consistent, high-performance access to data and supports a variety of development needs, including the creation of front-end database clients and middle-tier business objects, using applications, tools, languages, or Internet browsers. ADO is designed to be the one data interface needed for one-to-multitier client/server and Web-based data-driven solution development.

ADO provides an easy-to-use application-level interface to OLE DB, which provides the underlying access to data. ADO is implemented with a small footprint, minimal network traffic in key scenarios, and optimized interaction between the front end and data source — all to provide a lightweight, high-performance interface. ADO is easy to use because it is called using a familiar metaphor — the COM automation interface, available from all leading RAD, database tools, and languages on the market today. And because ADO was designed to combine the best features of — and eventually replace — RDO and DAO, it uses similar conventions with simplified semantics to make it a natural next step for today’s developers.

Design Goals for Microsoft Data Access Components

Microsoft® Data Access Components (MDAC) is a set of redistributable technologies that implement the Universal Data Access strategy. MDAC consists of the latest versions of ActiveX Data Objects (ADO), OLE DB components, and Open Database Connectivity (ODBC), which have now been released as an integrated set. Developers creating client/server and Web-based data-driven solutions select the components they need and call them from their choice of tools, applications, languages, or Web browser to create complete database solutions.

In designing MDAC, Microsoft set a number of guiding project goals, including the following:

  • Meet the key customer requirements outlined in the section “Customer Requirements for Data Access Technologies” in this paper. These requirements include performance, reliability, strongly committed vendors, and broad industry support. Discussions of how Universal Data Access and MDAC meet these requirements are provided in separate sections later in this document.

  • Enable access to the broadest possible range of data sources, focusing on those that are most important to customers now and in the future. To address this goal, we’ve taken a three-pronged approach:
    • Provide the best possible reuse of existing ODBC drivers, so organizations can continue to leverage their investments in these technologies. Continue to enhance strategic ODBC drivers from Microsoft as OLE DB components become broadly available.

    • Build OLE DB providers for the most important Microsoft and non-Microsoft data sources.

    • Create a comprehensive strategy for enabling the rapid development of high-quality, high-performance third-party OLE DB components to expose all mainstream data sources now and in the future.
  • Provide a migration path that will enable customers to leverage their investments in and experience with DAO, RDO, and ODBC.

These are the project goals guiding the development of the Microsoft Data Access Components to support and enhance the Universal Data Access strategy.

Next, we will take a closer look at each of the key customer requirements for data access technologies — performance, reliability, strongly committed vendors, and broad industry support — and detail specifically how Universal Data Access and MDAC meet these requirements.

Universal Data Access Is a High-Performance Architecture

We have seen that performance is of paramount concern to developers and users of data access technologies. Universal Data Access consequently has been designed with performance as its number one goal. This section will examine how the Universal Data Access technologies support this requirement and the positive implications for users.

Flexible, Component-Based Services Model

OLE DB, designed to be a high-performance architecture, accomplishes this by using a flexible, component-based services model. Rather than having a prescribed number of intermediary layers between the application and the data, OLE DB requires only as many components as are needed to accomplish a particular task. For example, suppose a user wants to run a query. Consider four scenarios:

  • The data resides in a relational database for which there currently exists an ODBC driver, but no native OLE DB provider. The application uses ADO to talk to the OLE DB Provider for ODBC, which then loads the appropriate ODBC driver. The driver passes the SQL statement to the DBMS, which retrieves the data.

  • The data resides in Microsoft® SQL Server™ or another data source for which there is a native OLE DB provider. The application uses ADO to talk directly to the OLE DB provider for Microsoft SQL Server. No intermediaries are required.

  • The data resides in Microsoft® Exchange Server, for which there is an OLE DB provider, but which does not expose an engine to process SQL queries. The application uses ADO to talk to the Microsoft® Exchange data provider and calls upon an OLE DB query processor component to handle the querying.

  • The data resides in the Microsoft NTFS file system in the form of documents. Data is accessed by using a native OLE DB provider over Microsoft® Index Server, which indexes the content and properties of documents in the file system to enable efficient content searches.

In all four cases, the application can query the data. The user’s needs are met with a minimum number of components. In each case, additional components are used only if needed, and only the required components are invoked. This demand-loading of reusable and shareable components greatly contributes to high performance when OLE DB is used.

Increasing Development Efficiency with OLE DB

ODBC has been a very important and successful data access standard. Now OLE DB has an improved architecture that provides a significant advantage over ODBC. Providersno longer have to implement a SQL relational engine to expose data. With ODBC, services such as cursoring and query processing need to be implemented by every ODBC driver writer. This represents overhead both for the ODBC driver author and for the end user. (How many cursor engines and query processors do you need on one machine?) With OLE DB, reusable service components handle the processing chores for a variety of data providers. OLE DB simplifies the process of writing data providers, which means they should come online faster and be of a higher quality. It also reduces the number of components installed on data consumer machines.

ADO Performance Advantages

As with OLE DB, ADO is designed for high performance. To achieve this, it reduces the amount of solution code developers must write by “flattening” the coding model. DAO and RDO, the object models that preceded ADO, are highly hierarchical models. To return results from a data source, the programmer has to start at the top of the object model and traverse down to the layer that contains the recordset. The ADO object model is not hierarchical. The programmer can create a recordset in code and be ready to retrieve results by setting two properties, then execute a single method to run the query and populate the recordset with results. The ADO approach dramatically decreases the amount and complexity of code that needs to be written by the programmer. Less code running on the client or middle-tier business object translates to higher performance. ADO also offers better performance in getting data from recordsets. Scalability is improved by minimizing overhead in simple scenarios.

Minimal Network Traffic in Key Internet Scenarios

Microsoft has designed OLE DB and ADO for the Internet, implementing a “stateless” model in which client and server can be disconnected between data access operations. MDAC contains a Remote Data Service component that provides efficient marshaling of data between the middle tier or server and the client, including support for batch updates, as well as an efficient client-side cursor engine that can process data locally without constant server requests. Thus MDAC provides greater local functionality and higher performance for Internet applications than previous versions and other approaches.

Ultimately, the performance of Microsoft Data Access Components will be judged in comparison with that of native access methods. The goal for OLE DB is to establish it as the native interface to major Microsoft and non-Microsoft data stores. To accomplish this, performance tuning for all key scenarios (transactions, decision support, etc.) and with major data sources will be of paramount concern. The number one design goal for ADO and OLE DB is performance, and the architectural foundations to achieve this are in place.

Solutions Built with Universal Data Access Components Are Reliable

As we have seen, reliability is one of the primary requirements for organizations managing and supporting data access applications. Universal Data Access aims to address this need in three areas: increasing the manageability of client-side components, enabling strong run-time coordination and control capabilities by the server, and delivering well-tested components.

Increasing the Manageability of Client-Side Components

One of the most important tools that organizations can use to increase reliability and decrease support costs is the reduction of the number of components to support on client PCs. The Universal Data Access strategy and the Microsoft Data Access Components support this approach in several key ways:

  • Universal Data Access supports new multitier and Web deployment models, in which data access logic and business logic are centralized on middle-tier servers. Front ends provide presentation services by using browser-based interfaces, or by using custom and packaged applications. In this model, application functionality is mainly centralized, not distributed to end-user PCs, thus reducing the number of components to manage on those machines.

  • Microsoft Data Access Components, version 1.5, which includes OLE DB and ADO, ships as part of these Microsoft system products:
    • Components for easy client deployment to Microsoft® Internet Explorer 4.0 and Windows 98

    • Full server components to Microsoft® Internet Information Services 4.0 (also known as the Option Pack for Microsoft® Windows NT® 4.0)

    Because OLE DB and ADO ship like system components, organizations can frequently rely on these components being available in a run-time environment and don't have to manage their distribution and maintenance.

  • ADO is tool- and language-independent and in many cases may be able to replace multiple data access libraries on client PCs. Organizations that may have previously supported DAO, RDO, and ODBC on each PC as part of a two-tier system will now be able to get the same functionality by deploying Microsoft® Internet Explorer 4.0 to their clients and using a three-tier architecture. Data access functions previously handled by ODBC clients will move into their middle-tier objects or their servers. Internet Explorer with ADO can thus replace DAO, RDO, and ODBC on client PCs.

Enabling Strong Server-Side Coordination and Control

Universal Data Access enables transactional control of diverse data sources and components by using Microsoft® Transaction Server (MTS). To achieve this, OLE DB data sources must implement the functionality of a resource manager, which handles transactions local to the data source and enables each data source to participate in distributed transactions. Microsoft Transaction Server provides a distributed transaction coordinator that guarantees atomic operations spanning multiple data sources using a reliable, two-phase commit protocol, and enables applications to scale as user load grows with minimal additional effort.

Delivering Well-Tested Components

MDAC components are rigorously tested. Because ADO and OLE DB have been shipping in high volume since the release of Internet Information Server 3.0, they have enjoyed significant field usage. With the advent of MDAC, and the commitment to ship ADO and OLE DB in a synchronized fashion, these components are now developed and tested side by side. Finally, after testing the components for interoperability, Microsoft stress-tests MDAC components with the products with which they ship (such as Internet Information Server and Internet Explorer) to guarantee reliable behavior in multithreaded, continuous operation in highly concurrent environments. This testing is designed to help ensure high-performance, highly reliable components that work well in a variety of real-world scenarios.

The net result of the Universal Data Access architecture’s three-pronged approach should be significant reductions in configuration and support expenses and a reduced total cost of ownership — in short, a reliable data access architecture.

Microsoft Commitment to Universal Data Access

The choice of data access technologies is extremely strategic for organizations. Typically other factors, such as the DBMS, largely drive this choice. However, customers have told us that data access decisions are made for the long haul, and consequently need to be considered carefully.

Here are some of the questions that customers have told us they need answered when evaluating a data access vendor:

  • Is the vendor here to support me, not just for the current release, but through a string of releases?

  • Does the vendor have a strong and compelling strategy for data access, and does that strategy mesh with the goals of my organization?

  • Has the vendor withstood the test of time by consistently delivering strong products in these markets?

  • Likewise, has the vendor been able to succeed in these markets, thus proving its ability to deliver technological solutions and build market momentum, resulting in broad support for the technologies?

Based on market share, developer acceptance and broad industry support for many technologies, we believe that Microsoft has consistently met this set of criteria and has proved to be a market leader for data access technology. We’ll now examine Microsoft commitment to Universal Data Access.

A Short History of Microsoft as a Data Access Vendor

A look at how the Universal Data Access strategy has evolved at Microsoft will help illuminate the long-term commitment the company is making in this area.

Microsoft began investing in data access shortly after the initial release of Microsoft SQL Server 1.0 in 1989. Initial interest in Microsoft SQL Server was high, but the tools available to program it were limited. The SQL standard was in its infancy, but was clearly bound to the coming client/server revolution. Microsoft knew that acceptance of client/server architecture would be highly beneficial and could see that the biggest problem the industry faced was the proliferation of data access interfaces, and the complexity of creating, maintaining, and programming against them.

Open Database Connectivity was the result of these factors. ODBC combined important features to make it extremely attractive:

  • It was SQL-based, making it familiar to most database developers of the day.

  • Its call-level interface supported a broad range of tools.

  • Its plug-in model for database drivers simplified implementation.

As ODBC gained broad support as a standard for data access, it became clear that a standards body should be defining its future. Microsoft turned over the specification for ODBC to the SQL Access Group, made up of a broad range of DBMS, middleware, and tools vendors.

Despite its features, ODBC had a number of shortcomings, which by 1993 were being addressed in the next phase of data access market development. ODBC was programmed as a Windows API, which made it difficult for the majority of customers to use it. A number of Microsoft tools and applications could use ODBC through the Microsoft® Jet database engine, but ODBC functionality was not extensible except for a few API-level programmers. Thus high-level programming models were created, first Data Access Objects and then Remote Data Objects, which simplified the ODBC programming model and made it accessible to a wider range of programmers.

DAO provided Microsoft® Access and Microsoft® Office programmers with an interface to the Jet database engine. RDO provided programmers using the Microsoft® Visual Basic® programming system with higher-level interfaces to ODBC. These interfaces seemed like natural extensions to the Visual Basic language used in each of these products, and have gained broad usage among database programmers.

By 1995, two major new trends began to shape the next phase of development. These two trends, which are still evolving, are the rise of the Internet as a database applications platform and the rise in importance of nonrelational data, which does not directly fit the database model encapsulated by ODBC.

One might wonder at this stage why the Internet requires new data access technologies. After all, the fundamental goal of connecting people with data remains the same. The Internet, however, presents new data access challenges on many levels:

  • Scale. The Internet is synonymous with more people accessing more data. This amplifies the priorities for effective data management: Performance, reliability, and security take on new importance.

  • Higher volume of clients. Because of the high volume, the Internet needs a better paradigm to share server-side resources.

  • Distributed medium. The Internet is by definition a distributed medium, with many more potential points of failure and network performance issues that go beyond the boundaries typically relevant to information technologists. On the flip side, the medium presents many opportunities to distribute processing tasks for improved service.

  • Casually connected clients. Client and server, previously bound tightly in time and space, are now virtually independent. Client computers need to be able to accomplish more with less support from the server, and at times, without a server connection.

  • New business opportunities. The Internet opens up the possibility for employees, customers, suppliers, and business partners to connect on many levels.

  • New types of data. The Internet, while using existing data and databases in new ways, is also composed of massive amounts of text, images, and other media that will grow in importance. Successful organizations will manage new data types effectively and synthesize meaning from them.

As the Internet catalyzes a major paradigm shift in database management and data access, a related shift is occurring: the emergence of nonrelational data sources. While the Internet highlights the need for management of textual and graphical data, organizations today face a proliferation of data in a variety of DBMS and non-DBMS stores, including desktop applications, mail systems, workgroup and workflow systems, and others. Most established organizations face an even larger challenge: leveraging the data in mainframe and minicomputer flat files as they extend access to this information to intranet-based and Internet-based customers.

Data access today encompasses all of the issues traditionally addressed by DBMS systems, plus a range of new data types, new clients, and new access methods. The Universal Data Access strategy was created to meet this new generation of challenges by leveraging the successful strategies of the past and embracing the architectures of the future.

Universal Data Access Builds on the ODBC Foundation

Universal Data Access is a strategy that includes and builds on the successful foundation of ODBC. ODBC successes include the following:

  • Establishing a market standard for database access. There are more than 170 ODBC drivers available today, providing access to a broad range of data.

  • Achieving portability, so applications can scale to new database platforms as an organization’s requirements change.

  • Responding to customers’ needs by steadily adding new features and performance improvements to enable better database applications.

The most frequent customer issues surrounding ODBC are related to performance and configuration management, defined as matching database drivers on multiple machines with multiple back-end data sources. Microsoft is aware of these issues and will continue to address them through subsequent ODBC releases, including a new and significantly improved ODBC driver for Oracle.

Moving forward, ODBC is a supported technology under the Universal Data Access umbrella. ODBC in the short and medium term is the best way to access a broad range of relational DBMS-based data due to the high number of drivers available. During this period, with ODBC remaining as a mature technology and OLE DB components becoming available, Microsoft does not want to force customers to choose between the two architectures and make the ensuing trade-offs. Our goal is to enable customers to take advantage of existing ODBC technologies, while adopting the Universal Data Access architecture for new applications.

It was therefore by design that the evolutionary strategy for migrating from ODBC to OLE DB was created. The very first OLE DB provider released by Microsoft was the OLE DB Provider for ODBC. Applications are then written to the ADO or OLE DB interface, and the OLE DB Provider for ODBC connects to the ODBC data source. If an organization later decides to change data sources, add data sources, or change from the ODBC driver to a pure OLE DB provider for the existing data source, then the application can be adapted with minimal changes.

This evolutionary strategy for migrating from ODBC to OLE DB carries some additional important benefits. Because OLE DB is a component-based architecture with service components providing processing capabilities on an as-needed basis, and because ODBC data sources can expose their data as OLE DB, OLE DB service component features may be invoked against ODBC data. For example, the Find feature provides for sorting and filtering within a result set. Thus, the result set can be reused and further refined, without an additional round trip to the server. This capability is not available for an ODBC client. Thus new and existing applications can gain additional data access features by using OLE DB to call broadly supported ODBC drivers.

This is the evolutionary path from ODBC to OLE DB that Microsoft is providing based on consistent customer feedback. Organizations should continue to plan on broad availability and support for ODBC drivers. As they build new applications, they should look to the Universal Data Access architecture, using ADO and OLE DB interfaces. Nonrelational data will be exposed by OLE DB providers. For relational data, organizations may choose between ODBC drivers, and, as they become available, OLE DB providers and components. In the long run, Microsoft believes customer demand will drive the market for OLE DB components and they, too, will become broadly available. Able to freely choose among and mix ODBC and OLE DB components, organizations will benefit from the highest possible application performance and reliability, while gaining new capabilities at a pace that suits their unique requirements.

Universal Data Access Is Strategic for Microsoft

The Universal Data Access strategy is intertwined with most of the major lines of business where Microsoft is serving organization customers, including operating systems, tools, applications, and Internet products. Universal Data Access is designed to work consistently across each of these major product lines, to enable organizations to leverage their data access expertise across teams and projects to build high-performance database solutions accessible to employees, customers, and business partners.

One of the strongest examples of this can be seen in ADO, which provides a single interface to data whether it’s being called from a developer tool, Web page, Office or a custom business object. Applications using a variety of front ends may now use the same high-performance interface to data, featuring a small memory footprint, demand-loaded reusable OLE DB components, and a familiar semantic derived from most widely used Microsoft interfaces. No matter where in the multitier architecture one is writing data access code, the interface can be the same — ADO.

Making integrated access to all forms of relational and nonrelational data ubiquitous is strategic for Microsoft products because it enables those products to add value through tools that use the Universal Data Access architecture. Customers are the ultimate beneficiaries as their tools and applications become more highly adept at processing the information they work with every day.

Relationship of Universal Data Access and Windows DNA

The Windows Distributed interNet Applications architecture is the Microsoft architectural framework for building modern, scalable, multitier distributed computing solutions that can be delivered over any network. Windows DNA provides a unified architecture that integrates the worlds of client/server and Web-based application development. Microsoft Universal Data Access, a central part of the Microsoft Windows DNA strategy, provides data access services for Windows DNA applications.

Figure 3. Windows Distributed interNet Applications architecture

Windows DNA addresses requirements at all tiers of modern distributed applications: user interface and navigation, business process, and storage. The core elements of the Windows DNA architecture are these:

  • Pluggable software components

  • Extensible Web browser and application server

  • Richly integrated platform services

  • Scalable distributed operating environment and servers

  • Open protocols and published interfaces

  • Choice of programming languages, tools, and hardware platforms

Because Microsoft Universal Data Access is based on COM, it provides a unified, consistent, and common data access model for all applications built to the Windows DNA model.

Broad Industry Support for Universal Data Access

Organizations using data access components have indicated that in order to invest in the Universal Data Access architecture, they need to see the support of vendors of related products and technologies. For customers, broad industry support carries many benefits — safety in numbers, availability of skilled people to work with the products, and products that work together without expensive integration and customization. This section details the activities in which Microsoft is engaged to solidify and publicize the broad range of companies supporting Universal Data Access.

Supporters of Universal Data Access

The industry reception for Universal Data Access has been very positive. Companies building components in each architectural segment recognize key benefits for their customers — improved performance and functionality, flexibility, and reduced cost. This section will detail the industries and key vendors supporting Universal Data Access, discuss Microsoft strategy for continued growth in industry support and provide instructions for additional federated vendors to join the Universal Data Access strategy.

The key industry segments supporting Universal Data Access are as follows:

  • DBMS vendors. These vendors benefit from Universal Data Access by gaining additional high-performance clients for their engines. Their customers benefit by gaining a broader choice of development tools and other supporting technologies and by connecting to and integrating with data from more sources.

  • Development tools vendors. Tools vendors benefit from Universal Data Access by being able to provide access to a broader range of data sources, thus enabling their customers to build richer, more functional applications. Leading tools vendors are participating by licensing MDAC, which includes ADO, OLE DB components and providers, and ODBC components and drivers. Tools vendors have thus gained the ability to enable their customers to create applications that access the vast majority of data sources available today.

  • Data access component builders. By building OLE DB components, data access component builders gain the benefits of a broadly accepted standard environment in which to deploy their products. Their customers can access more data, find more readily available support and interoperate with a broad array of products across the platform.

Because the list of vendors in each of the above categories is growing rapidly, the reader is asked to visit http://www.microsoft.com/data/ for a complete, updated list. Leading vendors in each industry segment are represented among the list of Universal Data Access supporters.

OLE DB Provider Strategy

To be successful, OLE DB must gain a broad array of native providers and components so that users can connect to virtually any data source, reuse OLE DB service components, and realize performance and reliability benefits.

The tools that OLE DB provider and component vendors use to simplify their work are found in the Data Access SDK. In addition to the data access consumer components discussed in this paper (ADO, OLE DB, and ODBC), users of the SDK receive additional tools, documentation and specifications to help them create high-performance OLE DB components. Provider writers will find the following:

  • Leveling specification. Defines the minimum level of functionality that should be implemented by every provider. In addition, it defines the level of functionality that most consumers will expect and that should be implemented by a provider when supported natively by the data source. To tie these together, it also defines the services that are likely to be supported by various classes of service components.

  • Conformance tests. Includes interface tests to show that a provider correctly implements the interfaces that it supports. Also includes a minimal set of ADO tests that show that a provider works well when in an ADO application. The initial focus of these tests is on the minimum provider interfaces as defined in the leveling specification.

  • OLE DB Simple Provider (OSP) Toolkit. Gets you started to build quick providers for tabular and other simple data.

  • Microsoft® Visual C++® development system template classes for OLE DB providers. These are a part of Visual C++ and are not actually included in the Data Access SDK.

  • OLE DB providers for Microsoft® Jet, SQL Server, and Oracle data sources.

These tools simplify the process of writing OLE DB components, provide a framework for creating components that interoperate in well-defined ways, and provide criteria by which OLE DB consumers can easily compare component features. Anyone interested in creating OLE DB components should obtain the Data Access SDK.

Data Access SDK

The Microsoft Data Access SDK is a set of tools and samples. It is designed to help developers create solutions using MDAC. The SDK provides a convenient single source for everything needed to learn about and create data access solutions.

The SDK contains MDAC version 2.0, tools for getting started with data access component development, tools for testing and distributing components, and documentation. The SDK is activity-based with content designed specifically for both consumer and provider writers, and for developers working with various languages and deployment environments.

How Vendors Participate in Universal Data Access

Microsoft is interested in working with vendors of products that support Universal Data Access to help ensure that components address the performance and quality demands of our joint customers. Vendors of DBMS products, development tools, and OLE DB service components should visit http://www.microsoft.com/data/ for updated information on programs, products and services.

How Universal Data Access Supports Data on Multiple Platforms

While the Microsoft® Windows NT®/Microsoft® Windows® 2000 operating system is emerging as an important platform for database management, many organizations rely on a mixture of operating systems and database platforms. To be successful, any strategy for providing data access must be equally efficient at accessing data on all major platforms. Universal Data Access provides the foundation for supporting efficient and reliable access to data on today’s major computing platforms. Microsoft is actively engaged in supporting third-party development projects involving OLE DB providers for non-Windows-based data. In fact, products using the Universal Data Access architecture to access leading DBMSs on non-Windows platforms are currently available.

Figure 4. Universal Data Access Supports Data Across the Enterprise

Because the OLE DB specification defines interfaces that components support, rather than providing a set of DLLs or actual system components, it is highly portable to other operating environments. OLE DB is based on the COM architecture, which is the Windows object model. This would seem to imply that OLE DB components must run on a Windows-based or Windows NT/Windows 2000–based PC; however, this is not the case. OLE DB in fact has two separate approaches that provide portability to non-Windows-based DBMS platforms: a full port of COM, available today from several vendors, and implementations of COM interfaces on non-Windows platforms.

Today ISG International Software Group Ltd. (http://www.isgsoft.com/), with its ISG Navigator product, provides an example, using the second approach described above, of MDAC components that integrate data from Windows NT/Windows 2000 and several other platforms, including the following:

Operating Environments | Database Platforms
Windows 95 Windows NT (Intel and Alpha) HP UX IBM RS/6000 AIX DEC UNIX SUN Solaris DEC OpenVMS (Alpha and VAX) IBM MVS (planned) | Microsoft SQL Server Oracle Sybase Informix RMS C-ISAM CA-Ingres DB2 Adabas (planned) IMS/DB (planned) RdbVSAM (planned) MUMPS (planned)

The important thing to recognize about the ISG Navigator product is that its availability and performance prove the ability of the Universal Data Access architecture to integrate data between Windows and non-Windows platforms. It is not the only approach to satisfying the need for multiplatform data access, but it is a solid implementation available and demonstrable today. The Navigator demo, available to anyone visiting the ISG Web site, demonstrates several important features:

  • An Internet-based front end using ADO code called from Visual Basic Scripting Edition in an Active Server Page

  • ADO taking advantage of OLE DB components on both Windows and non-Windows platforms

  • Navigator, in a single SQL query, joining data from multiple data sources, potentially running on several different platforms

Broad availability of MDAC components that integrate data on multiple platforms will benefit organizations that support multiple DBMS platforms. An additional benefit enables users to take advantage of new OLE DB capabilities when accessing non-Windows-based data. Powerful new service components, running on front ends or middle-tier servers, can be integrated with an OLE DB provider, including those running on non-Windows platforms. For example, general-purpose query processors, cursor engines, or custom business objects can all add value to non-Windows-based data exposed by OLE DB. Mainframe and UNIX-based databases that previously did not support remoting of data — an essential feature for the Internet and loosely connected scenarios — may now implement it, thus gaining greater use from existing systems and applications. This powerful extensibility and reusability model is a benefit of component-based software written to a broadly supported specification such as OLE DB.

How Universal Data Access Differs from Other Strategies

A number of leading DBMS vendors have begun shipping new databases and updated versions that follow “universal database” strategies. Customers may be curious about how those strategies differ from Universal Data Access.

In the other approaches, data from across the organization is consolidated in the DBMS, and the DBMS is extended with additional processing capabilities to handle new data types. This strategy can be attractive for several reasons:

  • It centralizes data for more efficient management.

  • It places a DBMS “wrapper” around new data types, which can ensure the protection of DBMS security and transaction services, while offering other potential benefits such as content indexing.

  • Many DBMSs are very efficient at serving up data, so organizations can generally expect good performance for applications based on traditional and nontraditional DBMS data.

Microsoft, while recognizing these benefits, believes they may be difficult for some organizations to attain. A universal database approach may require expensive and time-consuming movement to and maintenance of corporate data in the DBMS. It may require tools and applications to support it. And it may require compromises in the selection of supporting products. Customers’ applications will need to either implicitly support this architecture, which is unlikely, or be customized to integrate with it, which could be expensive.

It is very important to note that because Universal Data Access does not exclude any data stores, the two strategies can cooperate. In fact, OLE DB providers for a number of new “universal database” products are currently under development. Using the Universal Data Access strategy, customers will be able to use data in their existing databases, universal database servers, desktop applications, mainframes, etc. Organizations that combine Universal Data Access and universal database products will ultimately benefit from a broad choice of best-of-breed tools, applications, and DBMS products available from leading data access vendors.

Universal Data Access: A Road Map for the Future

Going forward, Microsoft has two important vehicles for shipping the supporting components of its Universal Data Access strategy: the Microsoft Data Access Components and the Data Access SDK. Long-term planning and development for both of these products are under way, and customers may be curious as to the future directions for the Universal Data Access strategy expressed in these plans.

MDAC

The mission for Microsoft Data Access Components is to provide in a consolidated release, the key data access technologies used across Microsoft tools, applications, and platform products. The next MDAC release will be shipped concurrently with the next release of Office. Each subsequent release will be tested and supported for use with the latest versions of following Microsoft products1:

  • Windows 95 and later

  • Windows NT version 4.0 and Windows 2000

  • Internet Explorer version 4.0 and later

  • Internet Information Server version 4.0 and later

  • Microsoft® Visual Studio® (and each of its tools) version 6.0 and later

  • Microsoft® Office 97 and later

There are three project-level design goals for the next release of MDAC:

  • Continue to deliver more and better OLE DB components for all major Microsoft data stores and strategic third-party stores.

  • Respond to input from vendors using the conformance tests to make them an industry de facto standard for measuring any OLE DB providers.

  • Continue to expand ADO. There are two aspects of this goal. One, to further superset the capabilities of DAO and RDO, so that developers working with Visual Basic and Microsoft® Office will have the option of moving to ADO without sacrificing current functionality. Two, to enable new scenarios, including more remote, disconnected capabilities and more sophisticated data models.

Conclusion

Organizations of all sizes today are creating business solutions that leverage data from the desktop to the enterprise. As the types of data and types of access have proliferated, the challenge to create business advantage has remained paramount. Microsoft has designed the Universal Data Access strategy to meet the needs of today’s distributed, multiplatform organization building client/server and Web-based data-driven solutions. By building in performance and reliability features, by making Universal Data Access a key part of the Windows DNA architecture, and by enlisting the support of a broad range of industry players, Microsoft is aggressively meeting customer needs.

Universal Data Access helps organizations build on existing systems and data stores as they create new client/server and Web-based solutions. Universal Data Access bridges the gap between existing systems and new technologies to create an evolutionary path for cost-conscious customers. As customers forge new business opportunities, Microsoft will be there to provide tools and technologies to enable success.

Footnote

1 Not all releases will support back versions of all listed products. For specific system requirements, please consult the MDAC product information, located at http://www.microsoft.com/data/.


Implementing ADO with Various Development Languages

The ADO Rosetta Stone

By Don Willits

July 1998

Microsoft® ActiveX® Data Objects (ADO) is a language-neutral object model that is the keystone of Microsoft's Universal Data Access strategy. Because it is language neutral and accommodates a variety of development languages, it is difficult to present the practical usage for each environment. This paper is a discussion of the complexities inherent to using ADO within various languages and implementation mechanisms within those languages.

To facilitate this discussion of ADO, take a lesson from the Rosetta Stone, a slab of black basalt that provided scholars with their first significant key to deciphering ancient Egyptian hieroglyphics. The Rosetta Stone was discovered in 1799 and appeared to contain one message (a group of priests praising their rulers for eliminating overdue taxes in the year 196 BC) in three languages: Greek, demonic characters, and Egyptian hieroglyphics. Because scholars knew Greek, they enthusiastically attempted to use the Rosetta Stone to decipher the hieroglyphics. Twenty years later, French scholar Jean Champollion produced a dictionary of Egyptian hieroglyphics after rigorous comparison of the hieroglyphics to the Greek.

In the spirit of Champollion's accomplishment the following pages describe how to implement ADO applications using Microsoft® Visual Basic® for Applications (VBA); Microsoft® Visual C++® (Visual C++); Java; Microsoft® Visual Basic® Scripting Edition (VBScript), and Microsoft® JScript®. In this case, VBA is the "Greek text"; you can use it as the base from which to learn about the other languages.

Numerous code listings are included in the text, as well as two sets of code examples in an accompanying zip file. The first set contains all the code listings presented in this document within working projects built with Microsoft® Visual Studio® 97 and the Data Access 2.0 SDK (which includes ADO 2.0). In each of these examples, you will find files named "LISTING" which contain the actual code samples. The second set, the Rosetta Stone samples, implement several common ADO scenarios and are ported to a variety of development languages and environments.

The paper was written against ADO 1.5 and ADO 2.0 using Visual Studio 97. Updates will be provided, as appropriate, for new features introduced with subsequent releases of either product. The latest version of this paper is available at http://www.microsoft.com/data.

This white paper does not discuss the ADO object model or its objects, methods, properties, or syntax. If you are not already familiar with ADO, refer to the ADO documentation for more information.


COM as the Heart of ADO Development

Microsoft's Component Object Model (COM) is central to all ADO development. When you use ADO, you use COM, regardless of the language you have chosen for the application. In particular, ADO is an Automation Server, and you use COM to invoke and utilize ADO objects.

Some languages hide the implementation details of COM more than others do, so you don't more than a beginner's understanding of COM to proceed. However, as your expertise with COM grows, so does your ability to use ADO efficiently and with more complexity. VBA hides many of the details of COM development, so you might choose VBA for your ADO applications. Visual C++, Java, and web-based scripting do not provide as much native COM support, but they do offer you more low-level control than what you might find with VBA.

The following are key areas of COM that you might want to be familiar with. In addition to understanding foundational COM concepts, these subjects directly affect your ability to develop an ADO application:

  • How the language or implementation you are using allows you to manipulate COM objects.

  • The fundamentals of implementing a COM-based application within your chosen language or development environment.

  • The ability to read a type library and with it, how collections are used within ADO.

  • Using and manipulating COM data types, specifically BSTR, VARIANT, and SAFEARRAY.

  • Handling error conditions via ADO.

For more information about these topics, see parts two and three.

For developers new to both COM and ADO, it may seem like a double learning curve to get started with ADO. This is true, but once you master the COM fundamentals needed for ADO, you are ready to implement applications utilizing other COM components. As you are learning ADO techniques, try them first in VBA, then try them in Visual C++ or Java. This will help you determine whether any difficulties you encounter are in your understanding of COM or ADO.


Part One: Getting Started with ADO

This paper discusses the following implementations of ADO applications across Microsoft development languages and environments:

  • VBA as implemented in either Microsoft® Visual Basic® 5.0 or Microsoft® Access 97.

  • Visual C++ with its three COM implementation mechanisms: #import, MFC OLE, and the Microsoft® Windows® API.

  • Java and the two mechanisms for creating Java classes around an Automation Server: the Java Type Library Wizard and the Microsoft® SDK for Java.

  • Scripting for server-side (Active Server Pages) or client-side (HTML) Web pages in either VBScript or JScript.

For each language or implementation discussion, a rudimentary ADO sample program demonstrates how to open and close a Recordset object.

Manipulating an automation object takes two steps:

  1. Define and instantiate a variable to be used to manipulate a COM object.

  2. Instantiate an actual instance of a COM object and assign it to the variable.

Each scenario discusses implementation-specific reasons why you might want to break out each step into a separate line of code or combine them into a single statement.

Requirements for the Samples

Most of the code examples and code listings require that you have the Data Access SDK 2.0 installed on your computer (which installs ODBC 3.51, OLE DB 2.0, ADO 2.0, and data access components). One code example requires Microsoft® SQL Server™ for invoking a stored procedure. Other applications may be required to support demonstration of various code listings (such as Visual C++).

To use the code examples, you must install the Access ODBC Driver and create a Data Source Name (DSN) called "ADODemo" for the Access MDB (ADODEMO.MDB) file provided with the examples. To create a DSN, use the ODBC Data Source Administrator in Control Panel.

The ADODEMO.MDB file contains a single table, Authors, which was extracted from the bibliography sample database that ships with Visual Basic. Most of the sample code in this paper refers to ADODEMO.MDB, with a few exceptions: some reference the Authors table in the PUBS database that ships with SQL Server.

For each of the samples, the following connection string opens ADODEMO.MDB, using the default userid and password for an Access database.

DSN=AdoDemo;uid=admin;pwd=;

Each sample's connection string uses the OLE DB Provider for ODBC to retrieve data from the MDB file via the Access ODBC Driver. You can specify a different OLE DB provider by adding "PROVIDER=…" to the connect string.

VBA

The easiest way to use ADO is within the context of a development environment that exposes VBA, such as Visual Basic or Microsoft® Access. Several features eliminate the complexities of ADO development and let you focus just on ADO.

  • Command line completion offers you a convenient way to navigate through the ADO type library and learn what it contains. This ranges from exposing the automation objects and enumerated types defined within the type library, to selecting a method or property of an object. The command line completion feature is unique to VBA in the Visual Basic 5.0 or Access 97 development environments.

  • To fully utilize the type library of a given automation server, VBA leaves out optional arguments for the methods of an ADO object and uses the default values for any argument within a method.

See the sections, "Benefits of Reading the ADO Type Library" and "COM Datatypes" in Part Two to learn techniques you can use to keep your COM skills sharp while still taking advantage of the strengths of VBA with COM.

The following ADO code snippet demonstrates how to open a Recordset object in VBA.

Listing 1: Opening an ADO Recordset in VBA

Dim Source As String
Dim Connect As String
Dim Rs1 As New ADODB.Recordset

Source = "SELECT * FROM Authors"
Connect = "DSN=AdoDemo;UID=admin;PWD=;"

Rs1.Open Source, Connect
Rs1.Close
Set Rs1 = Nothing
MsgBox "Success!"

In one line, the Dim statement defines a variable Rs1 as the Recordset object and ensures implicit instantiation of an instance of the Recordset COM object with the New attribute. Two strings are defined for both the query statement and the connection string. ADO requires BSTRs for these two arguments of Recordset.Open; however VBA automatically converts its String type into the BSTR required by ADO. After the Recordset object is opened, it is closed and the recordset variable is released, deallocating the actual ADO object that was created by the New clause.

Defining and Instantiating ADO objects with VBA

To create an automation variable and instantiate an object for that variable, you can use two forms of syntax: the CreateObject method or the Dim statement. With the CreateObject method, each statement is a discrete action. However, you can both define a variable and assign it to an instantiated object in one step as shown in Listing 2.

Listing 2: Instantiating an ADO Connection Object within VBA

' Technique #1: Use CreateObject() to create Connection object.
' Declare variable.
Dim conn1
' Implicit instantiation.
Set conn1 = CreateObject("ADODB.Connection.2.0")

' Technique #2A: Use Dim ... as ... to create Connection object
' for a pre-defined variable.
' Declare variable.
Dim conn2 As ADODB.Connection
' Implicit instantiation.
Set conn2 = New ADODB.Connection

' Technique #2A: Use Dim ... as ... New to create Connection object.
' Declare variable & implicit instantiation.
Dim conn3 As new ADODB.Connection

CreateObject is slower than using the Dim statement, is not strongly typed, and is not supported by command line completion. However, it does not require you to specifically reference ADO in your project, and it can instantiate a specific version of an ADO object (assuming that you have provided a valid PROGID that can be found in the Windows registry).

The Dim statement is only successful if you have already added the ADO type library to your project's references. You can use the Dim statement with VB because it has integrated COM support, including command line completion. In VBScript, you can only use the Dim statement to declare objects of type variant, but CreateObject is compatible with VBScript syntax.

Using the "ADODB.<object>" syntax is not strictly necessary with the Dim statement. It is a good idea, however, because using the "ADODB.<object>" prefix ensures that you won't have naming conflicts if you reference other type libraries whose objects might be the same as an object in the ADO type library.

ADO in VBA with Visual Basic 5.0

To create an ADO project in Visual Basic 5.0:

  1. With the ODBC Data Source Administrator in Control Panel, create a user DSN named "AdoDemo" using the Microsoft® Access Driver against the Access database ADODEMO.MDB. Then start Visual Basic 5.0.

  2. From the File menu, select New Project. From the list of new projects, select "Standard EXE." This creates a project and a blank form. From the File menu, select Save Project to save both files.

  3. Add a reference to the ActiveX Data Objects type library. From the Project menu, select References. In the References dialog box, ensure the following options for Visual Basic 5.0 are selected, and click OK.
    • Visual Basic for Applications

    • Visual Basic runtime objects and procedures

    • Visual Basic objects and procedures

    • OLE Automation

    • Microsoft ActiveX Data Objects 2.0 Library
  4. By default, the toolbox should be visible. (If not, from the View menu select Toolbox). Click the Command Button icon in the toolbox and draw a button on the form. Double click on the button, which both creates an event handler for that command button as well as places you in the code editor for that handler.

  5. Within the event handler, add the code from Listing 1 to the event handler of the command button. Notice the command line completion feature assisting you at several points.

  6. From the Run menu, select Start With Full Compile. Click the button you placed on the form.

ADO in VBA with Microsoft Access 97

You can manipulate ADO just as easily within Microsoft® Access 97 as in Visual Basic 5.0 because both use the same VBA engine. However, adding ADO to a Microsoft® Access application does not let you use the same features of Access with ADO as it exposes for Data Access Objects (DAO).

Note If you have code that uses SQL syntax to make changes in a datastore, and you migrate that code from DAO to ADO, you might have to change some of the syntax, particularly double quote characters. For more information, see "Single vs. Double Quote in SQL Statements" in Part Three.

To create an ADO project in Access:

  1. With the ODBC Data Source Administrator in Control Panel, create a user DSN named "AdoDemo" using the Microsoft® Access ODBC Driver against the Access database ADODEMO.MDB (supplied with the Rosetta Stone samples). Then start Microsoft® Access 97.

  2. From the File menu, select New Database. From the General tab, select Blank Database. Click OK.

  3. Select the Modules tab and click New.

  4. Add a reference to the ADO type library. From the Tools menu, select References. Make sure the following options for Microsoft® Access 97 are checked, and click OK. The Forms 2.0 Object Library is optional but useful for creating an interface, as demonstrated by the Access Rosetta Stone code example.
    • Visual Basic for Applications

    • Microsoft® Access 8.0 Object Library

    • Microsoft DAO 3.5 Object Library

    • Microsoft ActiveX Data Objects 2.0 Library

    • Microsoft Forms 2.0 Object Library
  5. From the File menu, select Save, name the module, and click OK. Close the module.

  6. In the dialog box for your database, select the Forms tab and click New to create a new form. From the selection of wizards for creating the form, select Design View.

  7. By default, the toolbox should be visible. (If it is not, go to the View menu and select Toolbox.) Click the Command Button icon in the toolbox and draw a button on the form. If the Command Button Wizard comes up, cancel it.

  8. Right click on the command button and select Build Event. In the Choose Builder dialog box, select Code Builder and click OK. This creates an event handler for that command button and places you in the code editor for that handler.

  9. Within the event handler, add the code from Listing 1 to the event handler of the command button. Close the form.

  10. On the Forms tab, select your form and click Open. Click the button you placed on the form.

Visual C++

There are three ways to manipulate ADO within Visual C++: #import, the ClassWizard in MFC OLE, and COM in the Windows API. Of the three, #import is the most powerful and allows you to generate code that is nearly identical to VBA in syntax.

Before you can invoke an ADO object, you must initialize OLE. Listing 3 shows a global declaration you can add to one of the C source files in your project to ensure that OLE is properly initialized and terminated.

Listing 3: Automatic Initialization and Termination of OLE

struct InitOle {
InitOle()  { ::CoInitialize(NULL); }
~InitOle() { ::CoUninitialize();   }
} _init_InitOle_;

ADO in Visual C++ with #import

When you provide #import with the path or name of a file containing a type library, it generates definitions for GUIDs, class wrappers for each ADO object, and enumerated types based on what it finds in the type library. For any type library you reference, Visual C++ generates the following two files at compile time:

  • A header file (.TLH extension) that contains enumerated types and definitions for objects contained in the type library.

  • An implementation file (.TLI extension) that has wrappers for each method in the type library's object model.

For example, if you generate #import on msado15.dll, Visual C++ creates msado15.tlh and msado15.tli.

The #import directive also makes use of a new class, _com_ptr_t, also known as a smart pointer. Smart pointers automatically perform the COM QueryInterface, AddRef, and Release functions. In this respect, using #import on a COM object model begins to generate code similar to that which you see in VBA. The _com_ptr_t class and the other helper classes available with #import are discussed in greater detail later in this white paper.

Failed HRESULT results returned by ADO cause #import to raise an exception. The _com_error class raised in the exception automates the task of querying the IErrorInfo interface to acquire details of the error. If you do not want exception handling in your code, you can override #import. For details, see the Knowledge Base article q175784 (INFO: Over-riding #import's Exception Raising Mechanism).

The #import directive generally does not support default values or optional arguments. This is actually a design limitation, because #import allows you to use default values if the argument is of type VARIANT. However, very few methods where default values would most likely be used in ADO have arguments of type VARIANT.

Early releases of the exception handling mechanism in #import have a bug wherein the mechanism does not correctly release the IErrorInfo interface when an error is raised through or by ADO. A three-line fix implemented in the ImportErrorEx() function within the Rosetta Stone code examples corrects this bug. For details, see the Knowledge Base article q73645 (BUG: Access Violation in Msdaer.dll with _com_error Exceptions).

The following code demonstrates how to use #import to add support for ADO in your application:

#import "c:\program files\common files\system\ado\msado15.dll" \
      rename ( "EOF", "adoEOF" )

Renaming the EOF is required because in a typical Visual C++ application, EOF has already been defined as a constant (-1) using #define. However EOF is also defined within ADO as a property of type VARIANT_BOOL. Without the rename attribute redefining the string EOF for what #import generates on ADO's type library, you would get a compiler error, because it would replace the name of the property EOF with –1. The compiler does not accept this as a valid variable name and returns compiler error messages as a result.

You could achieve the same results with this line of code:

#import <msado15.dll> rename ( "EOF", "adoEOF" )

This technique is the more modern approach and requires that you already added the path "c:\program files\common files\system\ado" to one of your PATH, INCLUDE, or LIB environment variables (assuming that the operating system has been installed on the C: drive). Or you can add the path to the options in Visual C++, as demonstrated in the section, "Creating an ADO Project with #import".

This alternate syntax eliminates the need for the rename attribute:

#undef EOF
#import <msado15.dll>

This assumes you will not actually need the EOF definition. You could alternately assign EOF to a temporary variable, undefine EOF, import ADO, redefine EOF using the temporary variable you saved, and then undefine that temporary variable.

Defining and Instantiating ADO objects with #import

Manipulating an Automation object takes two steps:

  1. Define and instantiate a variable to be used to manipulate a COM object.

  2. Instantiate an actual instance of a COM object and assign it to the variable.

With #import you can accomplish both steps in a single line of code, using the smart pointer's (_com_ptr_t) constructor to pass in a valid CLSID, or PROGID. You could also declare an instance of the object, then use the _com_ptr_t::CreateInstance() method to instantiate the object. Both techniques are demonstrated in Listing 4.

Listing 4: : Instantiating an ADO Connection Object with #import

#import <msado15.dll> rename( "EOF", "adoEOF" )
...
struct InitOle {
InitOle()  { ::CoInitialize(NULL); }
~InitOle() { ::CoUninitialize();   }
} _init_InitOle_;
...
// Method #1: Declaring and instantiating a Connection object
_ConnectionPtr  Conn1( __uuidof( Connection ) );

// Method #2: Declaring and instantiating a Connection object
_ConnectionPtr  Conn1 = NULL;
HRESULT         hr    = S_OK;

hr = Conn1.CreateInstance( __uuidof( Connection ) );

The recommended technique is the second one because the constructor of _com_ptr_t does not return a failed HRESULT if something goes wrong. The first method is flawed because it cannot test if the creation of the ADO Connection object succeeded or failed.

In both cases, use the Visual C++ extension __uuidof( Connection), which in this case retrieves a GUID defined by #import in the .TLH file corresponding to the ADO Connection object. By passing it to CreateInstance, you create a valid ADO Connection object for the Connection smart pointer. There are other forms you could pass into either the constructor or CreateInstance to reference the ADO Connection object and accomplish the same result. For more information, see the Visual C++ documentation for topics about #import.

The only flaw with the code above is that it assumes you have imported only ADO. If you import multiple libraries and one or more of those libraries have an object with the same name, you must provide some differentiation between the two. For example, both ADO and DAO contain an object named Recordset.

Another attribute of #import, no_namespace, prevents the compiler from qualifying the classes in a namespace, which is to say the name of the library the type library defines. In the case of ADO, this is ADODB. Using no_namespace means you don't have to reference the namespace when initializing or defining variables whose types are defined by what #import generates. However, if you have many type libraries imported into your application, it is safer to omit the no_namespace attribute.

Listings 5 and 6 show the difference in your code with and without the no_namespace clause. While it may be more work to omit no_namespace, it does ensure your code will be more robust in case it uses other Automation Servers whose objects might share the same name as an object found in ADO.

Listing 5: Using #import with the no_namespace Attribute

#import <msado15.dll> no_namespace rename( "EOF", "adoEOF" )

void main()
{
   HRESULT         hr    = S_OK;
   _ConnectionPtr  Conn1 = NULL;
   hr    = Conn1.CreateInstance( __uuidof( Connection ) );
   Conn1 = NULL;
}

Listing 6: Using #import without the no_namespace Attribute

#import <msado15.dll> rename( "EOF", "adoEOF" )

void main()
{
   HRESULT                hr    = S_OK;
   ADODB::_ConnectionPtr  Conn1 = NULL;
   hr    = Conn1.CreateInstance( __uuidof( ADODB::Connection ) );
   Conn1 = NULL;
}

Creating an ADO project with #import

The following ADO code snippet demonstrates how to get started with #import.

Listing 7: Opening an ADO Recordset in Visual C++ with #import

#include <windows.h>
#import  <msado15.dll> rename("EOF", "adoEOF")
... Init Ole ...
void main()
{
   HRESULT                hr = S_OK;
   ADODB::_RecordsetPtr   Rs1 = NULL;
   _bstr_t Connect( "DSN=AdoDemo;UID=admin;PWD=;" );
   _bstr_t Source ( "SELECT * FROM Authors" );

   hr = Rs1.CreateInstance( __uuidof( ADODB::Recordset ) );
   Rs1->Open( Source, Connect,
            ADODB::adOpenForwardOnly,
            ADODB::adLockReadOnly, -1 );
   Rs1->Close();
   Rs1 = NULL;
   ::MessageBox( NULL, "Success!", "", MB_OK );

Disciplined COM developers may find the idea of setting a smart pointer to NULL to force an implicit release of the COM object somewhat disconcerting. Smart pointers make this unnecessary, but you may find it reassuring to explicitly release your objects rather than just setting them to NULL or letting them go out of scope.

The code declares an instance of the ADO Recordset smart pointer named Rs1, but doesn't instantiate the actual ADO Recordset object until later with the CreateInstance method. Note that #import generates the smart pointer definitions in the .TLH file with the _COM_SMARTPTR_TYPEDEF macro, as follows:

_COM_SMARTPTR_TYPEDEF(_Recordset, __uuidof(_Recordset));

To determine the name of any smart pointer that #import generates for use in your code, examine the list of _COM_SMARTPTR_TYPEDEF macros generated in the .TLH file and add a "Ptr" to have the name of the smart pointer class created by the macro. So _Recordset in this definition equates to the smart pointer derived class _RecordsetPtr. The use of an underscore preceding an ADO object is not required; it depends on how the object was defined in the ADO type library.

The code defines two BSTRs for both the query statement and the connection string using the #import helper class, _bstr_t. #import also offers a helper class for managing VARIANTs: _variant_t. The samples based on #import use _variant_t extensively; it will be discussed in depth later in the whitepaper. After the Recordset object is opened, it is closed and the recordset variable is released, deallocating the actual ADO object that was created by the CreateObject method.

Finally, there is the fact that CreateInstance is the one time you could receive a failed HRESULT from #import. Anytime you call a method of an object wrapped by a smart pointer, it raises a Visual C++ exception with an instance of the _com_error class. The following macro combines the fact that robust code should already be checking for exceptions raised by #import and eliminates the need to specifically test the HRESULT.

#define CREATEINSTANCE(sp,riid) \
   { HRESULT _hr =sp.CreateInstance( __uuidof( riid ) ); \
      if (FAILED(_hr)) _com_issue_error(_hr ); }

Usage for this macro is as follows:

      CREATEINSTANCE( conn1, Connection )

To create a Visual C++ 5.0 application that can utilize ADO with the #import pre-compiler directive:

  1. With the ODBC Data Source Administrator in Control Panel, create a user DSN named "AdoDemo" using the Microsoft® Access ODBC Driver against the Microsoft® Access database ADODEMO.MDB (supplied with the Rosetta Stone samples). Then start Visual C++ 5.0.

  2. From the File menu, select New. In the New dialog box, select the Projects tab, and select MFC AppWizard (exe) from the list. Enter ADO in the Project name box. Click OK.

  3. You are now in the MFC AppWizard. To accept the defaults, click Finish. Click OK in the New Project Information dialog box and wait for Visual C++ to finish generating your project.

  4. From the Tools menu, select Options and select the Directories tab. In the Show Directories For box, select Library Files. Add the following path to the list of directories:
    C:\program files\common files\system\ado

This assumes you installed the Windows directory on your C: drive. This directory should contain the MSADO15.DLL file, which contains the ADO type library. Click OK to close the Options dialog box after adding this path.

  5. Add a reference to the ActiveX Data Objects type library. From the File menu, select Open and choose the STDAFX.H file generated by the MFC AppWizard. Click Open. Just after the #include directives, add a new line:
    #import <msado15.dll> rename( "EOF", "adoEOF" )
  6. If you named your project ADO, then there will be a class named CAdoApp. From the File menu, select Open and choose the implementation file for this class, ADO.CPP and click Open. Add the code from Listing 3 to ensure OLE is initialized.

  7. Add the code from Listing 7 to the CAdoApp.InitInstance() method for that class, just prior to the return statement.

  8. From the Build menu, select Rebuild All.

ADO in Visual C++ with MFC OLE

MFC OLE, like #import, generates class wrappers for a type library. Unlike #import, MFC OLE does not generate enumerated types from the type library, but it does implement ADO cleanly. The CString and COleVariant MFC classes hide BSTRs and VARIANTs. However, your application carries along all of MFC as overhead (the MFC42.DLL), as opposed to the much leaner #import. Each class wrapper built by MFC OLE is derived from the ColeDispatchDriver class, and failed HRESULTS generated by ADO are wrapped inside the ColeDispatchException class.

Creating an ADO project with MFC OLE

The following ADO code snippet demonstrates how to get started with MFC OLE. Listing 8 assumes you've already used the MFC ClassWizard to generate classes against ADO 2.0.

Listing 8: Opening an ADO Recordset in Visual C++ with #MFC OLE

AfxOleInit();
...
_Recordset  Rs1;
COleException   e;

COleVariant Connect( "DSN=AdoDemo;UID=admin;PWD=;" );
COleVariant Source ( "SELECT * FROM Authors" );

Rs1.CreateDispatch( "ADODB.Recordset.2.0", &e );
Rs1.Open( (VARIANT) Source, (VARIANT) Connect, 0, 1, -1 );
Rs1.Close();
Rs1.ReleaseDispatch();
AfxMessageBox("Success!");

The AfxOleInit() function is an MFC helper which initializes COM. The code declares an instance (Rs1) of the ADO Recordset class wrapper derived from COleDispatchDriver, but doesn't instantiate the actual ADO Recordset object until later with the CreateDispatch method. The name of a class wrapper generated by the ClassWizard is identical to the name of the object in the type library. Note the use of an underscore preceding an ADO object is not universally required; if the object is defined with an underscore in the ADO type library, then the smart pointer class will require one.

The code defines two ColeVariants for the Connect and Source variables. ColeVariant has an operator that can cast to the BSTR type required by ADO for the Recordset.Open method. After the Recordset object is opened, it is closed and the recordset variable is released, deallocating the actual ADO object that was created by the CreateDispatch method.

To create a Visual C++ 5.0 application that can utilize ADO with the MFC OLE ClassWizard:

  1. With the ODBC Data Source Administrator in Control Panel, create a user DSN named "AdoDemo" using the Microsoft Access ODBC Driver against the Access database ADODEMO.MDB (supplied with the Rosetta Stone samples). Then start Visual C++ 5.0.

  2. From the File menu, select New. In the dialog box, select the Projects tab, and in the list on that tab, select MFC AppWizard (EXE). Type ADO for the project name.

  3. In the MFC AppWizard, click Finish to accept the defaults. Click OK in the New Project Information dialog box and wait for Visual C++ to complete generating your project.

  4. From the Tools menu, select Options and select the Directories tab. In the Show Directories For box, select Library Files. Now add the following path to the list of directories:

C:\program files\common files\system\ado

This assumes you installed the Windows directory on your C: drive. This directory should contain the MSADO15.DLL file, which contains the ADO type library. Click OK to close the Options dialog box .

  5. From the View menu, select ClassWizard. Click Add Class and select From A Type Library... in the menu that appears.

  6. In the Import From Type Library dialog box, select the file msado15.dll from C:\program files\common files\system\ado.

  7. In the Confirm Classes dialog box, select all of the classes listed and click OK. After Visual C++ finishes generating the wrapper classes around the ADO type library, click OK in the MFC ClassWizard dialog box.

The ClassWizard generates two files, MSADO15.H and MSADO15.CPP, with wrapper classes derived from COleDispatchDriver for each ADO class you selected in the Confirm Classes dialog box.

  8. If you named your project ADO, then there will be a class named CAdoApp. From the File menu, select Open and choose the implementation file for this class, ADO.CPP and click OK. Add the code from Listing 3 to ensure OLE is initialized.

  9. Add the code from Listing 8 to the CAdoApp.InitInstance() method for that class, just prior to the return statement.

  10. From the Build menu, select Rebuild All.

ADO in Visual C++ with COM Functions

Both #import and MFC OLE provide wrapper classes around a given Automation object which are derived from a root class, _com_ptr_t or COleDispatchDriver, respectively. You could, though, bypass these wrappers and directly manipulate the Windows Application Programming Interface to utilize ADO. Using _com_ptr_t or ColeDispatchDriver is COM programming, but both classes encapsulate and hide much of the more common invocations of COM APIs such as CoCreateInstance, or using QueryInterface, AddRef, and Release on a given COM interface. This section discusses what it would take to bypass these wrapper classes and use the Win32 API directly to manipulate a COM object.

To use ADO with COM directly, you need two header files (ADOID.H and ADOINT.H) provided by the OLE DB 1.X SDK (for ADO 1.0 and 1.5) or the Microsoft Data Access SDK 2.0 (for ADO 2.0). These two files define the CLSIDs, interface definitions, and enumerated types that you need to manipulate the ADO type library. Though you do not link to a library, you must also use a #include statement on the <INITGUID.H> header.

Creating an ADO project with COM API

The following ADO code snippet demonstrates how to get started with COM API. Listing 9 assumes you installed the OLE DB 1.X SDK or the Data Access SDK on your computer.

Listing 9: Opening an ADO Recordset in Visual C++ with COM API

#include <windows.h>
#include <initguid.h>  // Include only once in your application
#include "adoid.h"     // ADO GUID's
#include "adoint.h"    // ADO Classes, enums, etc.
... INIT OLE ...
void main()
{
   HRESULT          hr  = S_OK;
   ADORecordset*    Rs1 = NULL;
   VARIANT          Source;
   VARIANT          Connect;

   VariantInit( &Source  );
   VariantInit( &Connect );

   Source.vt       = VT_BSTR;
   Source.bstrVal  = ::SysAllocString( L"SELECT * FROM Authors");

   Connect.vt      = VT_BSTR;
   Connect.bstrVal = ::SysAllocString( L"DSN=AdoDemo;UID=admin;PWD=;" );

   hr = CoCreateInstance( CLSID_CADORecordset,
                     NULL,
                     CLSCTX_INPROC_SERVER,
                     IID_IADORecordset,
                     (LPVOID *) &Rs1 );

   if( SUCCEEDED( hr ) ) hr = Rs1->Open( Source,
                                          Connect,
                                          adOpenForwardOnly,
                                          adLockReadOnly,
                                          -1 );
   if( SUCCEEDED( hr ) ) hr = Rs1->Close();
   if( SUCCEEDED( hr ) ) { Rs1->Release();  Rs1 = NULL; }
   if( SUCCEEDED( hr ) ) ::MessageBox( NULL, "Success!", "", MB_OK );

Listing 9 demonstrates the inclusion of <INITGUID.H> in order for the application to run with MSADO15.DLL. Prior to ADO 1.5, a .LIB file was provided to ensure your application would use ADO; however it is unnecessary when you use 1.5 and include <INITGUID.H>. You can only include <INITGUID.H> once, or you will get multiple LNK2005 errors when building your application.

The code declares a Recordset interface variable and sets it to NULL, instantiating an ADO object with the call to CoCreateInstance(). ADORecordset is a definition provided by ADOINT.H. The code defines two VARIANTs and assigns to them BSTR values for the query and connection information. After opening the Recordset object, the code closes it, releases the ADO Recordset object, and sets the interface variable to NULL.

Testing for a failed HRESULT is a significant difference between this code and #import or MFC OLE which relies upon an exception being raised. See the section about error handling for more information.

To create a Visual C++ 5.0 application that can utilize ADO with the COM API:

  1. With the ODBC Data Source Administrator in Control Panel, create a user DSN named "AdoDemo" using the Microsoft Access ODBC Driver against the Access database ADODEMO.MDB (supplied with the Rosetta Stone samples). Then start Visual C++ 5.0.

  2. From the File menu, select New. In the dialog box, select the Projects tab, and select MFC AppWizar (exe) from the list. Type ADO for the project name.

  3. You are now in the MFC AppWizard. Click Finish to accept the defaults. Click OK in the New Project Information dialog box and wait for Visual C++ to complete generating your project.

  4. From the Tools menu, select Options and select the Directories tab. In the Show Directories For box, select Include Files. Add the following path to the list of directories:
    C:\msdasdk\include\adoc

This assumes you installed the Data Access SDK 2.0 on your C: drive.

  5. If you named your project ADO, then there will be a class named CAdoApp. From the File menu, select Open and in the implementation file for this class, ADO.CPP, add the code from Listing 3 to ensure OLE is initialized.

  6. Add the code from Listing 9 to the CAdoApp.InitInstance() method for that class, just prior to the return statement.

  7. From the Build menu, select Rebuild All.

Java

ADO functionality is exposed to Java developers through the com.ms.wfc.data.* classes that ship with the Microsoft Java VM. These Java classes extend the ADO interfaces and create notifications interesting to the Java programmer. The Java classes also expose functions that return Java types to the user. To improve performance, the Java class directly accesses the native data types in the OLE DB Rowset object, and returns them to the Java programmer as Java types without first converting them to and from a VARIANT. ADO has also been extended to work with event notifications in the WFC framework. The Microsoft SDK for Java contains reference information, samples, overviews, white papers, and tutorials related to integrating with ADO and other Microsoft technologies through Java. The Microsoft SDK for Java is available for use in developing Java applications, or with Visual J++ 6.0. It can be downloaded from http://www.microsoft.com/Java/.

ADO in Java

In Java, enumerated types are represented as integers defined in an interface that corresponds to each ADO enumeration. You can reference the values of an ADO enumerated type with the following syntax:

com.ms.wfc.data.AdoEnums.<Enum Name>.<constant Name>

An example of this is shown in the following code fragment for setting the CommandType property of a Command object:

Cmd1.putCommandType( com.ms.wfc.data.AdoEnums.CommandType.STOREDPROC );

The following ADO code demonstrates how to get started with using ADO in Java.

Listing 10: Opening an ADO Recordset in Java

import com.ms.wfc.data.*;
import com.ms.com.*;

public static void main( String args[])
{
   Recordset   Rs1 = new Recordset();
   Variant Source     = new Variant( "SELECT * FROM Authors" );
   Variant Connect    = new Variant( "DSN=AdoDemo;UID=admin;PWD=;" );
   int     LockType   = AdoEnums.CursorType.FORWARDONLY;
   int     CursorType = AdoEnums.LockType.READONLY;
   int     Options    = -1;

   Rs1.Open( Source, Connect, LockType,  CursorType, Options );
   Rs1.Close();

   System.out.println( "Success!\n" );
}

The code declares an instance of the ADO Recordset class and initializes it all on the same line of code. Further, it declares variables for each of the arguments in the Recordset.Open() method, especially for LockType and CursorType (because Java doesn't support enumerated types). It opens and closes the Recordset object.

To create a Visual J++ application that can utilize ADO with the Java Type Library Wizard:

  1. With the ODBC Data Source Administrator in Control Panel, create a user DSN named "AdoDemo" using the Microsoft Access ODBC Driver against the Access database ADODEMO.MDB (supplied with the Rosetta Stone samples). Then start Visual J++.

  2. From the File menu, select New. In the dialog box, select the Projects tab, and select Java Applet Wizard from the list. Type ADO for the project name and click OK.

  3. You are now in the Java Applet Wizard. To accept the default settings, click Finish. In the New Project Information dialog box, click OK.

  4. From the Project menu, select Settings. In the Debug tab, set the category to General, and select the Stand-alone interpreter (applications only) option in the Debug/Execute project under section. Click OK.

  5. From the File menu, select Open and select ADO.java and click Open. Add the code from Listing 10 within the ADO class generated by the wizard.

  6. From the Build menu, select Rebuild All.

Scripting

ADO and the Remote Data Service (RDS) were first released with Internet Information Server (IIS) and serve as mechanisms for manipulating databases within the scripting environment for either client or server.

Within an intranet or Internet environment, ADO is an excellent mechanism for server-side scripting to expose data. In this scenario, ADO, the underlying OLE DB provider it utilizes, and any other components needed to reference a given datastore are installed on a server running IIS. Using Active Server Pages (ASP), ADO is a component referenced in a script that generates static HTML, which is passed over an intranet to a client Web browser. Through the use of scripting, the Web page can send actions back to the server-side script, allowing the user to update, traverse, or view specific data. With session or environment variables, you can maintain persistence of ADO objects, although this will incur some overhead.

RDS is the right solution for remoting a recordset onto the client in the scripting environment. RDS consists of two parts, client and server-side components. On the server, RDS sits on top of ADO and allows you to generate a recordset and marshal it to the client. On the client side, RDS contains just enough DLLs to allow you to traverse that recordset in a disconnected state. You can still scroll through the recordset and even post changes made to that data with minimal impact on the network and back-end server. You should use RDS to provide a recordset to the client if client-side scripting is absolutely necessary (although further discussion of RDS and client-side scripting is beyond the scope of this white paper).

While Remote Data Objects (RDO) and DAO can be used with server-side scripting, neither have ADO's native support (via RDS) for remoting a recordset across the Internet or an intranet. Note that for the following discussion of server-side scripting, you should use IIS version 3.0 or later.

ADO in VBScript (on the Server)

Some of the differences between Visual Basic and VBScript are the supported data types, the scope of data, and how constants are created and used.

  • VBScript supports only the Variant data type, which can hold different types of data. In general, you can store the data you need in a Variant data type, and the data will function appropriately due to casting performed by VBScript. It recognizes the type required by ADO, and converts the value in the Variant accordingly.

  • VBScript supports some of the built-in Visual Basic functions such as Msgbox, Date, and IsNumeric. However, because VBScript is a subset of Visual Basic, not all built-in functions are supported. For example, VBScript does not support the Format function and the file I/O functions.

  • One of the biggest difference between VBA and VBScript is the fact you cannot Dim objects to a specific type in VBScript, but instead have to use the older CreateObject function call.

  • The New syntax used with Dim in VBA is not supported in VBScript. In addition, you cannot use on error goto <label> within VBScript, which hampers traditional error trapping mechanisms.

To see a list of Visual Basic features that are not supported by VBScript, see the Visual InterDev documentation, in particular the topic titled, "Visual Basic for Applications Features Not in VBScript."

One notable difference between scripting and non-scripting ADO code samples discussed so far is the ODBC Datasource. For non-scripting applications, you created a User DSN in the ODBC Data Source Administrator. For scripts which are running under an Internet Information Service, you must create a System DSN, or your scripts won't be able to recognize the datasource you created. This is true of any application using ODBC via NT Service.

Creating an ADO project with VBScript

The following ADO code demonstrates VBScript using server-side scripting.

Listing 11: Opening an ADO Recordset in VBScript

<%  @LANGUAGE="VBSCRIPT" %>
<%  Option Explicit %>
<!--#include File="adovbs.inc"-->
<HTML>
   <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

   <!-- Your ASP Code goes here -->
<%
Dim Source
Dim Connect
Dim Rs1

Source = "SELECT * FROM Authors"
Connect = "DSN=AdoDemo;UID=admin;PWD=;"

Set Rs1 = Server.CreateObject( "ADODB.Recordset.2.0" )
Rs1.Open Source, Connect, adOpenForwardOnly
Rs1.Close
Set Rs1 = Nothing
Response.Write("Success!")
%>
   </BODY>
</HTML>

The <% and %> tags ensure that you have a server-side script running in an Active Server Pages under IIS 3.0 or later. The type of a given variable is not declared; all three are implicitly Variant. Use Server.CreateObject to create an ADO Recordset object (and for a specific version of ADO). Conversions between VBScript's Variant and the datatypes ADO expects (such as BSTR for Recordset.Open) are implicit. Note that you must include adovbs.inc or else constants such as adOpenForwardOnly will not be defined.

Note For the following demonstration, you will not actually create any kind of project.

To create a VBScript that can utilize ADO with server-side scripting

  1. With the ODBC Data Source Administrator in Control Panel, create a system DSN "AdoDemoAsp" using the Microsoft® Access Driver against the Microsoft® Access database ADODEMO.MDB (supplied with the Rosetta Stone samples). Then start Visual InterDev.

  2. From the File menu, select New. In the dialog box, select the Files tab, and select Active Server Page from the list. Click OK.

  3. Save the ASP page as ADO.ASP in the location of the root directory for your Internet Information Server installation. Then copy the ADOVBS.INC file provided by the installation of ADO in \program files\common files\system\ado into that directory.

  4. Replace the ASP code generated by Microsoft® Visual InterDev® with the contents of Listing 11 .

  5. Use http://<servername>/ado.asp to run the page. This assumes you have used Internet Information Server to specify that the virtual directory serving as the root location of the Web site has been marked as Executable.

ADO in JScript (on the Server)

JScript is to Java what VBScript is to VBA. It allows you to use Java syntax in a scripting environment. The following sample ADO code demonstrates JScript using server side scripting.

Listing 12: Opening an ADO Recordset in JScript

<%
   var Source;
   var Connect;
   var Rs1;

   Source = "SELECT * FROM Authors";
   Connect = "DSN=AdoDemoASP;UID=admin;PWD=;"

   Rs1 = Server.CreateObject("ADODB.Recordset.2.0")
   Rs1.Open( Source, Connect );
   Rs1.Close();
   Rs1 = null;

   Response.Write("Success!")
%>

The <% and %> tags ensure that you have a server-side script running in an Active Server Page under Internet Information Server 3.0 or later.

Note For the following demonstration, you will not actually create any kind of project.

To create a JScript that can utilize ADO with server-side scripting:

  1. With the ODBC Data Source Administrator in Control Panel, create a system DSN "AdoDemoAsp" using the Microsoft Access ODBC Driver against the Access database ADODEMO.MDB (supplied with the Rosetta Stone samples). Then start Visual InterDev.

  2. From the File menu, select New. In the dialog box, select the Files tab, and select Active Server Page from the list. Click OK.

  3. Save the ASP page as ADO.ASP in the location of the root directory for your Internet Information Server installation. Then copy the ADOJAVAS.INC file provided by the installation of ADO in \program files\common files\system\ado into that directory.

  4. Replace the ASP code generated by Visual InterDev with the contents of Listing 12.

  5. Use http://<servername>/ado.asp to run the page. This assumes you have used Internet Information Server to specify that the virtual directory serving as the root location of the web site has been marked as Executable.

The Rosetta Stone Samples

The zip file accompanying this white paper includes implementation of an ADO sample that demonstrates the following:

  • Returns a recordset from a parameterized query against an Access Database.

  • For each ADO object that supports them, exposes the collection of dynamic and built-in properties.

  • Provides a "code template" (and helper functions) required for minimal error handling for errors raised either by ADO or by an underlying provider.

  • Creates and then executes a Stored Procedure against SQL Server that returns a recordset as well as output and return parameters.

Figure 1 shows the interface for the ADO Rosetta Stone sample ported to VBA within Visual Basic 5.0. While the exact appearance may vary from implementation to implementation, the functionality and rough appearance of the sample remains the same.

Figure 1: The ADO Rosetta Stone Sample Implemented with Visual Basic 5.0

Within the source code, the structure of the ADO Rosetta Stone Samples is standardized so filenames, methods, functions, procedures, and variables share the same names and as much as possible the same layout of comments and white space. Within most of the Rosetta Stone samples, you will find files with the following names:

  • ADOCORE: Contains the event handlers for the four separate routines that demonstrate ADO.

  • ADOUTILS: Contains helper routines for error handling or for code shared by more than one of the ADO event handlers.

  • LOG: Where necessary, contains advanced exception handling code, for Visual C++ and Java in particular.

The ADO Rosetta Stone sample has been ported to the following implementations:

  • VBA using Visual Basic 5.0

  • VBA using Microsoft® Access 97

  • Visual C++ via #import using Visual C++ 5.0

  • Visual C++ via MFC OLE using Visual C++ 5.0

  • Visual C++ via native COM using Visual C++ 5.0

  • Java using the Java Type Library Wizard in Visual J++ 1.1

  • Java using the Microsoft SDK for Java with Visual J++ 1.1

  • VBScript in a Server Side Script for Internet Information Server 4.0

  • JScript in a Server Side Script for Internet Information Server 4.0

For the ADOVJ sample, because there are no global variables and to ease readability, the empty VARIANT/BSTR and other variables were moved into the ADOCore class which contained the event handlers. In addition, in order to support exception handling and graceful cleanup, the Connection and Recordset Objects were explicitly created at the start of each method before entering the try/catch block.

  • The remainder of this section walks through the highlights of the ADO Rosetta Stone samples and uses VBA as the Greek text, leaving it up to you to examine the actual sample that matches the language you are using for implementation details not specifically discussed here. The code samples given below may have also been streamlined slightly from the actual Rosetta Stone samples, which are thoroughly commented.

Return a Recordset From a Parameterized Query

Return a Recordset From a Parameterized Query is implemented in a method named "Access" (or some mild derivation of such) for each of the Rosetta Stone samples.

Listing 13: Return a Recordset From a Parameterized Query

Set Cmd1 = New ADODB.Command
Set Cmd1.ActiveConnection = Conn1
Cmd1.CommandText = "SELECT * FROM Authors WHERE AU_ID < ?"

Set Param1 = Cmd1.CreateParameter(, adInteger, adParamInput, 10)
Param1.Value = 10
Cmd1.Parameters.Append Param1
Set Param1 = Nothing

Set Rs1 = Cmd1.Execute()

This VBA text takes advantage of optional arguments in the CreateParameter method, skipping the first argument altogether.

Dumping Built-In and Dynamic Properties

For each property you set on an ADO object, or each argument in a method, one or more dynamic properties in a properties collection will be initialized. ADO tests the underlying provider to ensure that the behavior you have requested, especially opening a Connection or Recordset object, is actually supported. Based on these tests, the corresponding dynamic or built-in property is initialized. Unlike other object models, ADO only initializes those properties it needs to test in order to complete a given action you specify in the source code. Therefore, the results you see in the properties collection of an object vary depending on how you have manipulated that object.

In addition, if ADO can't perform the action you requested (that is, one or more of the properties needed from the underlying data provider is not supported), then it downgrades your request and attempts to complete the action. This happens most frequently when you try to open a cursor that the underlying provider doesn't support. A cursor is defined by the combination of three properties: CursorLocation, CursorType, and LockType. ADO may downgrade either CursorType or LockType if the requested cursor is not available. Usually ADO 1.5 updates the property that was changed internally to reflect what you actually received. Examining the properties collection can give clues as to why the cursor was not exactly what you expected.

Exposing dynamic and built-in properties for each properties collection is implemented in a method named "ProviderProperties" (or some mild derivation of such) for each of the Rosetta Stone samples.

Listing 14: Dumping Built-In and Dynamic Properties

Public Sub Props()
   Dim Prop As ADODB.Property
   Dim Conn1 As New ADODB.Connection

   Conn1.ConnectionString = "DSN=AdoDemo;UID=admin;PWD=;"
   Conn1.Open

   For Each Prop In Conn1.Properties
      Debug.Print "Property " & Prop.Name
      Debug.Print vbTab & "   Type       = " & GetType(Prop.Type)
      Debug.Print vbTab & "   Value      = " & Prop.Attributes
      Debug.Print vbTab & "   Attributes = " & _
                                 GetPropertyAttributes(Prop.Attributes)
   Next Prop
End Sub

Sample Code Template Error Handling

Robust error handling is implemented in a method named "CodeTemplate" (or some mild derivation of such) for each of the Rosetta Stone samples. This method provides minimal error handling and demonstrates it works by attempting to open a connection without providing any connection information. Each of the Rosetta Stone samples uses the same error handling in each of the ADO routines. CodeTemplate merely provides an abbreviated version.

Because error handling itself varies so dramatically from language to language, consult the discussion in part two for more information.

Invoking a Stored Procedure Output and Return Parameters

This routine is written for the SQL Server ODBC Driver shipping with ODBC 3.0 and later. Prior to Version 3.0, the SQL Server ODBC Driver let you view return and output parameters while the Recordset object was still open. With ODBC 3.0 (and later), the driver contained several bug fixes which changed the driver's behavior to require that the Recordset object returned by the stored procedure be closed prior to viewing the output or return parameters.

Opening a stored procedure which returns both a Recordset object and output and return parameters is implemented in a method named "SQLServer" (or some mild derivation of such) for each of the Rosetta Stone samples.

Listing 15: Retrieving Output and Return Parameters

Set Cmd1 = New ADODB.Command
Set Cmd1.ActiveConnection = Conn1
Cmd1.CommandText = "sp_AdoTest"
Cmd1.CommandType = adCmdStoredProc
Cmd1.Parameters.Refresh
Cmd1.Parameters(1).Value = 10

Set Rs1 = Cmd1.Execute()
'… Process Rs1
Rs1.Close

Debug.print "RetVal Param = " & Cmd1.Parameters(0).Value
Debug.print "Input  Param = " & Cmd1.Parameters(1).Value
Debug.print "Output Param = " & Cmd1.Parameters(2).Value

WinDiff and the Rosetta Stone Samples

Because all the Rosetta Stone samples are similar, you can use the WinDiff utility that ships with Visual Studio and the Platform SDK to compare source code to observe the syntactic and logical differences between the samples. WinDiff lets you compare two individual files or all the files contained in two directories.

Figure 2: Comparing ADO in Visual C++ via #import to ADO in Java via Java Type Library Wizard

In Figure 2, WinDiff compares the #import Visual C++ and Java Type Library Wizard versions of the same ADO code. Areas in yellow (Java) or red (#import) show the differences between the two files, and areas with a white background are identical between the two files.

To use WinDiff to compare different sections of the Rosetta Stone samples:

  1. Open the WinDiff Utility. In this example we'll assume that the directory C:\ADOVC contains the three ADO Rosetta Stone samples for Visual C++.

  2. From the File menu, select Compare Directories. In the Select Directory dialog box, select the two directories to compare and whether to include sub-directories in the comparison. Enter the path of both directories to be compared.

  3. WinDiff provides a list of all the files listed in both directories and optionally sub-directories as well. Files displayed in red can be found in both paths and are different. Files displayed in black are either identical or unique to a given directory. In this case, double click on the "ADOCORE.CPP" file and look at the differences.

  4. The ADO Rosetta Stone samples were written to have identical comments and white space. This allows WinDiff to analyze and display the differences between the two files much more accurately.


Part Two: ADO and COM

Part Two discusses the following important elements you need to know about COM when developing ADO applications:

  • Reading the Type Library

  • ADO Type Library and COM Implementations

  • COM Datatypes

  • Error Handling with ADO

Reading the Type Library

Because ADO is a language-neutral Automation Server, the more you know about COM, the more you can accomplish with ADO. Even if you are programming in VBA, which hides many COM details, it helps to know COM. Whatever language you use, the ability to read and understand the ADO type library (contained within MSADO15.DLL for ADO 1.5 and later) greatly enhances your ability to fully utilize features of ADO.

Reading a type library is not hard, and there are excellent tools for doing so. VBA features an Object Browser in both Visual Basic and Microsoft® Access. You can use #import, which returns the contents of the type library in a .TLH file. (This method is useful for non #import Visual C++ developers so they can examine enumerated type definitions that MFC OLE might not otherwise provide.) Or you can use a language-neutral utility for viewing language-neutral object models: the OLE COM Object Viewer, which ships with Visual Studio.

To view a type library with the OLE-COM Object Viewer:

  1. Click Start, point to Programs, point to Microsoft Visual C++, and click OLE-COM Object Viewer.

  2. From the File menu, select View TypeLib…, and enter the path for the type library you wish to view, in this case MSADO15.DLL. Click Open.

Figure 3: Viewing the ADO Type Library

In this case, use the OLE-COM Object Viewer to examine the Command.Execute() method. You can observe from the type library that each of the arguments of Command.Execute() are optional, and that one has a default value. This is significant because not all COM implementation mechanisms take advantage of either optional arguments or default values, that is, you have to provide some value anyway. If you want to know what the default is, you may have to use the type library. For each of the languages and implementations discussed in Part One, a discussion of the use of default values and optional arguments will follow.

Hidden Type Library Elements, Default Values, and Optional Arguments

The type library viewer won't actually indicate which members of the ADO type library, be it values of enumerated type or actual interfaces, are hidden, and which ones are public. Command line Completion within VBA will not expose hidden elements of the type library, but you can view hidden members with the Show Hidden Members command in the Object Browser in VB. COM implementations for Visual C++ and Java will typically expose all members, hidden or not, in any class wrappers that are generated. Most of the time, though, you will not need to use or know about hidden members of the type library.

Viewing the type library is useful for determining default values of a given property or argument of a method. It also lets you determine which arguments are optional. The COM implementation you use may not support either defaults or optional arguments, that is, you may have to specify every argument when calling a method call yourself. Viewing the type library is useful for understanding what ADO expects, or rather, what more robust COM implementations provide for free.

Many of the ADO enumerated types typically correspond either to arguments within the methods of ADO objects, or to properties of ADO objects. Not all arguments and properties have corresponding enumerated types, but most do. For any given enumerated type, the valid values are listed, and optionally, two special values, Unknown and Unspecified.

Unknown is usually for a property in a read-only state, and is indicative of an object that hasn't been initialized or which is in an unknown state. Unspecified tells ADO to either use the default value for the property or argument, or to use the value previously assigned to that property. Unspecified is actually a hidden member of a given enumerated type.

There is one special case for Unknown used with the CommandTypeEnum property, but this is an exception and not fully in-line with the rest of the ADO specification for the use of Unknown values within an enumerated type.

Occasionally, the argument of a method will have a corresponding property with the same object that the method belongs to. This scenario is the exception, not the rule, but it is important to understand how Unspecified may affect the behavior you receive from ADO. As a rule, ADO will use the value in the method and ignore the value you set in the property, unless you used one of the Unspecified enumerated values.

The Command.Execute method demonstrates this, whose third argument also corresponds to the Command.CommandType property. Specifying adCmdUnspecified in the third argument of Command.Execute tells ADO to use the value in the CommandType property. Otherwise, the value in the Execute method will be used and the CommandType property will be ignored. This particular example is further compounded by the fact that it is one of the rare cases where ADO also allows you to specify an Unknown value for CommandType. In this case, giving ADO an adCmdUnknown in the CommandType property (assuming its not overridden by the Execute method's third argument) or in the Execute method's third argument tells ADO that the CommandText to execute could be a query statement, could be a table name or it could be a stored procedure name. ADO will try all three until one works.

Listing 16 gives some examples of how Unspecified and Unknown work with the Command object. This provides a definitive, if somewhat complex sample of how default values, optional arguments, Unknown, and Unspecified work together.

Listing 16: Executing a Command

' Technique #1
Set Cmd1.ActiveConnection = conn1
Cmd1.CommandText = "SELECT * FROM Authors"
' CommandType by default is unknown
Cmd1.Execute

' Technique #2
Set Cmd2.ActiveConnection = conn1
Cmd2.CommandText = "SELECT * FROM Authors"
Cmd2.Execute v1, , adCmdUnknown

' Technique #3
Set Cmd3.ActiveConnection = conn1
Cmd3.CommandText = "SELECT * FROM Authors"
Cmd3.Execute v1, , adCmdText

All three techniques accomplish the same goal, although using Unknown slows performance because ADO takes time to test the actual type of the CommandText provided. With Visual C++ or Java, you would have to specify values for each argument because COM implementations in these languages do not take advantage of either optional arguments or default values for method arguments.

Default Properties and Collections

At times, an element in the type library may be the "default" property of a particular object or collection. In this case, you wouldn't need to see or use the element unless the implementation you are using doesn't recognize it as a default. An example of this is the Item property and its usage in Collections.

Typically, to reference an item within a collection (such as Fields, Properties, and Parameters) you provide an index to the Item property of the collection. However the Item property is the default property of a collection, and in languages such as VBA, you can easily skip it. With command line completion, you won't even see that it is there. Visual C++ and Java developers do not have this support, and therefore need to know the item property of a collection exists and reference it.

Here is equivalent code given in VBA, Visual C++, and Java for retrieving the value of the first column in an open recordset. Note that because of VBA's support for recognizing the default member of a given object or property, it has the greatest number of ways, in one line of code, to retrieve the first column's value.

Listing 17: Comparison of the Item Property in VBA, Visual C++, and Java

' VBA Syntax
Dim v as variant

   v = Rs1.Fields.Item("Au_ID").Value
   v = Rs1.Fields.Item(0).Value
   v = Rs1.Fields("Au_ID").Value
   v = Rs1.Fields(0).Value
   v = Rs1("Au_ID").Value
   v = Rs1(0).Value
   v = Rs1!Au_ID

// C++ Syntax (using #import)
_variant_t v;

v = Rs1->Fields->GetItem( _variant_t( 0L ) )->Value;
v = Rs1->Fields->GetItem( _variant_t( "au_id" ) )->Value;

// Java Syntax
Variant v  = new Variant();
Variant v1 = new Variant();

v1.putInt( 0 );
v = Rs1.getFields().getItem( v1 ).getValue()

v1.putString( "Au_ID" );
v = Rs1.getFields().getItem( v1 ).getValue()

The ADO Type Library and COM Implementations

VBA

VBA typically exposes required elements of the type library using command line completion. Most of the time this provides you with all the information you need to use ADO. There are occasional exceptions, and the Object Browser in both Visual Basic 5.0 and Microsoft® Access 97 provide the equivalent of the OLE-COM Object Viewer for any type libraries you have referenced in your current application. This is useful because the IDE does not expose hidden members of the ADO type library with command line completion. You can enable the Show Hidden Members option of the Object Browser to view these otherwise unseen members of the type library. This is illustrated in Figure 4.

Figure 4: Viewing Hidden Members of the ADO Type Library

An example of a hidden member, as mentioned earlier, is the Unspecified value for many of the ADO enumerated types.

Visual C++

For Visual C++ the need to view the type library is dependent upon the mechanism you choose to implement ADO. You must specify valid values for each argument of a given method. None of the three mechanisms in Visual C++ take full advantage of default values or optional arguments:

  • The Visual C++ #import pre-compiler directive generates class wrappers and enumerated data types based on what it finds in the type library.

  • MFC OLE generates class wrappers that hide BSTR and VARIANT data types inside CString and COleVariant wrapper classes, respectively. MFC OLE, though, does not generate any definitions for a type library's enumerated types.

  • For developers using COM, reading the ADOINT.H file can be difficult. Using a type library viewer instead simplifies the definitions listed in ADOINT.H.

Java

ADO is exposed to Java through the com.ms.wfc.data.* classes that ship with the Microsoft virtual machine. They can be used like any other core Java library classes, with the same debugging, profiling, and coding techniques Java developers are familiar with.

Scripting

There is no inherent support for reading the type library or even taking advantage of command line completion with scripting.

COM Datatypes

ADO uses three COM datatypes, BSTR, VARIANT, and SAFEARRAY, that you need to handle. Depending upon the native COM support of the language you are using, this might have an impact on your code. Of the three, SAFEARRAY is probably the most challenging to learn, and COM provides excellent support for the SAFEARRAY.

VBA, VBScript, and JScript are not strongly typed, and each of these implementations feature a Variant data type. With VBA, you can use other types besides Variant; in VBScript, every variable is of type Variant. Visual C++ and Java are strongly typed, which places more responsibility on you to convert between datatypes.

Empty VARIANT and BSTR Values

Within Visual C++ or Java, you must create empty BSTR and VARIANTs for arguments with default values that VBA would allow you to safely ignore, or use Unknown or Unspecified enumerated type values.

Listing 18: The Need for Empty BSTR and VARIANT Values

' VBA Syntax
Conn1.ConnectionString = "DSN=AdoDemo;UID=admin;PWD=;"
Conn1.Open
Set Cmd1.ActiveConnection = Conn1
Set Rs1 = Conn1.Execute()

// C++ Syntax (using #import)

   _variant_t  vtEmpty (DISP_E_PARAMNOTFOUND, VT_ERROR);
   _bstr_t     bstrEmpty(L"");
   _bstr_t     Source(L"SELECT * FROM Authors");

   ADODB::_ConnectionPtr  Conn1 = NULL;
   ADODB::_RecordsetPtr   Rs1   = NULL;
   HRESULT hr    = Conn1.CreateInstance(__uuidof( ADODB::Connection ));

   Conn1->ConnectionString = "DSN=AdoDemo;UID=admin;PWD=;";
   Conn1->Open( bstrEmpty, bstrEmpty, bstrEmpty, -1 );
   Rs1 = Conn1->Execute(
      Source,
      &vtEmpty,
      ADODB::adCmdText );

   Rs1->Close();
   Conn1->Close();

   ::MessageBox( NULL, "Success!", "", MB_OK );

// Java Syntax
Variant      vtEmpty;
Variant      vtEmpty2;
String       bstrEmpty = new String("");

com.ms.wfc.data.Connection  Conn1 = new com.ms.wfc.data.Connection();
com.ms.wfc.data.Recordset   Rs1 = new com.ms.wfc.data.Recordset();
Variant     vtEmpty   = new Variant("");
String      bstrEmpty = new String("");
String      Source    = new String("SELECT * FROM Authors");

vtEmpty.VariantClear();

Conn1.setConnectionString( "DSN=AdoDemo;UID=admin;PWD=;" );
Conn1.open( bstrEmpty, bstrEmpty, bstrEmpty, -1 );

Rs1 = Conn1.execute( Source,
                     com.ms.wfc.data.AdoEnums.CommandType.TEXT);

Note that even though Connection.Open() requires four arguments and Command.Execute()requires three arguments, the VB code does not include them. With Visual C++ or Java, you may not have the luxury of default arguments being exposed by the classes that wrap ADO objects. In particular, you must define the empty VARIANTs using the parameters DISP_E_PARAMNOTFOUND and VT_ERROR; otherwise you may get errors when you attempt to use them with ADO.

The code listing above explicitly defines vtEmpty, however #import automatically creates a variant of the exact same type called vtMissing. You could easily use vtMissing in place of the definition for vtEmpty. vtMissing was defined to be used for optional arguments of any method in a COM type library.

Converting Variants to Native Datatypes

With Visual Basic, a loosely-typed language, it is simple to use Variants. In Java you can assign and retrieve other datatypes with a Variant fairly easily. With Visual C++, however, you must be more rigorous in your coding. With the Rosetta Stone samples, a function called CrackStrVariant converts a Variant into native datatypes. This function was originally included in the Visual C++ sample DAOVIEW and offers some code for converting Variants to native datatypes. It was written for MFC classes CString and COleVariant, but it can easily be adapted non-MFC classes. In fact COleVariant is just a class derived from the VARIANT data type. This code has also been included in the Rosetta Stone samples as a standalone code segment, CRACKVAR.CPP.

ANSI to UNICODE Conversions

With VBA and Java, you don't need to know if your strings are ANSI, UNICODE, and if they need to be converted. Visual C++, operating at a lower level, does require you to be aware of this. For #import developers, you can use _bstr_t and _variant_t to handle conversions for you implicitly. For MFC, CString::SysAllocString() converts an ANSI string to a UNICODE string. But if you are using COM directly without any helper classes such as _variant_t or COleVariant, you need to use the Microsoft® Win32® API to perform these conversions. You can borrow some MFC or #import code to help. In Listing 19, the CString::SysAllocString() method has been modified to be a standalone, MFC-independent function.

Listing 19: ANSI to UNICODE Conversion Helper

BSTR AnsiToBSTR( LPTSTR pchData )
{
   // Helper function derived from source of CString::AllocSysString()
   BSTR bstr = NULL;
#if defined(_UNICODE) || defined(OLE2ANSI)
   bstr = ::SysAllocStringLen(pchData, wcslen( pchData ) );
#else
   int nLen = MultiByteToWideChar(CP_ACP, 0, pchData,
                                 strlen( pchData ), NULL, NULL);
   bstr = ::SysAllocStringLen(NULL, nLen);
   if (bstr == NULL)
      return NULL;
   MultiByteToWideChar(CP_ACP, 0, pchData,
                        strlen( pchData ), bstr, nLen);
#endif

   return bstr;
}

This is but one technique you can use for creating a BSTR, and is useful for strings assigned or created dynamically during execution. Consider the COM code in Listing 9 that assigned a BSTR value to a VARIANT named Source. There are actually several ways you can accomplish this, as shown in Listing 20.

Listing 20: Ways to Create a BSTR from an ANSI string

VARIANT Source;
Source.vt       = VT_BSTR;
Source.bstrVal  = ::SysAllocString(L"SELECT * FROM Authors");
Source.bstrVal  = AnsiToBSTR( "SELECT * FROM Authors" );

Using SafeArrays

SafeArray is an Automation datatype used to pass data of varying types safely between client and server processes. Use them in ADO as optional ways to add records: with the AddNew and OpenSchema methods, and with the Parameters argument of the Command.Execute method.

SafeArrays are not complex, but can be more intimidating than other datatypes. VBA handles them invisibly with its Variant data type. Visual C++ handles them with the COM API and supports class wrappers such as COleSafeArray (MFC OLE). The Microsoft VM offers similar support in Java with its com.ms.com.SafeArray class. The following listings demonstrate how to use OpenSchema to retrieve column information in VBA, Visual C++, and Java.

Listing 21: Using SafeArrays with the OpenSchema Method in VBA

   Dim conn1 As ADODB.Connection
   Dim rs1 As ADODB.Recordset
   Dim vsa

   Set conn1 = New ADODB.Connection
   conn1.Open "DSN=AdoDemo;UID=admin;PWD=;"
   vsa = Array(Empty, Empty, "Authors", Empty)

   Set rs1 = conn1.OpenSchema(adSchemaTables, vsa)

   Do Until rs1.EOF
      Debug.Print "Table name: " & _
                     rs1!TABLE_NAME & vbCr & _
                     "Table type: " & rs1!TABLE_TYPE & vbCr
      rs1.MoveNext
   Loop
   rs1.Close
   conn1.Close

   MsgBox "Success!"

In this case, it is trivial to create a SafeArray with VBA. You define a Variant and then use the VBA Array function to build the array. With Visual C++, you have to do a bit more work as shown in Listing 22.

Listing 22: Using SafeArrays with the OpenSchema Method in Visual C++

   ADODB::_ConnectionPtr Conn1 = NULL;
   ADODB::_RecordsetPtr  Rs1   = NULL;
   HRESULT               hr    = S_OK;
   _variant_t  vtEmpty (DISP_E_PARAMNOTFOUND, VT_ERROR);

   hr = Conn1.CreateInstance( __uuidof( ADODB::Connection ) );
   Conn1->Open( L"DSN=Pubs;UID=sa;PWD=;", L"", L"", -1 );

   // Create elements used in the array
   _variant_t varCriteria[4];
   varCriteria[0] = vtEmpty;
   varCriteria[1] = vtEmpty;
   varCriteria[2] = L"Authors";
   varCriteria[3] = vtEmpty;
   const int nCrit = sizeof varCriteria /
                     sizeof varCriteria[0];

   // Create SafeArray Bounds and initialize the array
   SAFEARRAYBOUND rgsabound[1];
   rgsabound[0].lLbound   = 0;
   rgsabound[0].cElements = nCrit;
   SAFEARRAY *psa         = SafeArrayCreate( VT_VARIANT, 1, rgsabound );

   // Set the values for each element of the array
   for( long i = 0 ; i < nCrit && SUCCEEDED( hr );i++)
   {
      hr  = SafeArrayPutElement(psa, &i,&varCriteria[i]);
   }

   // Initialize and fill the SafeArray
   VARIANT vsa;
   vsa.vt = VT_VARIANT | VT_ARRAY;
   V_ARRAY(&vsa) = psa;

   // call OpenSchema
   Rs1 = Conn1->OpenSchema(ADODB::adSchemaTables,vsa);

   Rs1->Close();
   Conn1->Close();

Creating the SafeArray in Visual C++ takes several steps. First, create an array of VARIANTs and set them to the values required by the particular schema you are opening. Then create the boundary structure for the SAFEARRAY and use it to initialize the size and contents of the SAFEARRAY. Assign each element in the SafeArray from your VARIANT array. Finally, assign the completed SAFEARRAY as a member of a VARIANT type to pass as the second argument to the OpenSchema method. (While this example used the #import _variant_t class, you could just as easily use the VARIANT type.)

The following listing demonstrates how to create a SafeArray in Java.

Listing 23: Using SafeArrays with the OpenSchema Method in Java

   com.ms.wfc.data.Connection  Conn1 = new com.ms.wfc.dataConnection();
   com.ms.wfc.data.Recordset   Rs1 = new com.ms.wfc.data.Recordset();

   Conn1.open( "DSN=AdoDemo;UID=admin;PWD=;", "", "", -1 );

   Object[] sa = new Object[]{“”, ””, ”authors” ,””};
   Rs1 = Conn1.openSchema(com.ms.wfc.data.AdoEnums.Schema.TABLES, sa);

   System.out.println( "Success!\n" );

Error Handling with ADO

Regardless of the programming language you use, error handling with ADO deserves careful attention. Any error condition you encounter will be raised through COM to the native error handling mechanism you are utilizing. As a convenience, the Errors collection lists additional warnings exposed by the underlying provider.

The ADO Errors collection is available if you have instantiated an ADO Connection object. This collection is reset and catches errors any time an unexpected condition is raised by an underlying provider (data or service) used by ADO. In addition, components underlying the provider may raise errors back to the provider, which it passes to ADO. For example, errors encountered by ODBC are raised to the OLE DB Provider for ODBC and from there to the ADO Errors collection. A common misconception, though, is that errors ADO itself encounters are not placed in this collection but must instead be caught by whatever native error-handling mechanism the language or implementation you are using provides. Therefore your error handling must check both the ADO Errors collection as well as whatever native mechanism for raising and catching errors exists.

When an error condition is encountered, whether that error is raised to the ADO Errors collection or to your language's native error handling mechanism, you may see an error message in one of three formats, as shown in the following table. For example, the ADO error "No Current Record" is defined as having a numeric value of 3021. But it could also appear as an 8 digit hexadecimal value or as a negative long value. The table below presents all three valid results for 3021.

Table 1: Error Code Conversions

Error Code | Error Message
3021 | No Current Record
0x80040BCD | No Current Record
-2147218483 | No Current Record

0x0BCD is simply the hexadecimal conversion of the value 3021. The value shows up as 0x80040BCD because all ADO errors have 8004 prepended to them. The long negative value, –2147218483, is simply a LONG conversion of 80040BCD.

The remainder of this section discusses what you need to implement robust error handling in your application and includes code excerpts from the ADO Rosetta Stone samples.

VBA

With VBA you need to verify both the ADO Errors collection as well as information raised to the Err object. Listing 24 demonstrates how to implement ADO error handling with VBA.

Listing 24: ADO Error Handling with VBA

   msado15._Connection  Conn1 = new msado15.Connection();
   msado15._Recordset   Rs1 = new msado15.Recordset();

   Conn1.Open( "DSN=AdoDemo;UID=admin;PWD=;", "", "", -1 );

   SafeArray sa = new SafeArray(Variant.VariantVariant ,4 );
   sa.setString( 0, "" );
   sa.setString( 1, "" );
   sa.setString( 2, "authors" );
   sa.setString( 3, "" );
   Variant Vsa = new Variant( sa, false );

   Variant vtEmpty = new Variant();   // OpenSchema 3rd param
   vtEmpty.noParam();
   Rs1 = Conn1.OpenSchema(SchemaEnum.adSchemaTables, Vsa,vtEmpty);

   System.out.println( "Success!\n" );

Visual C++

When calling an ADO method, regardless of language used, an error condition is typically returned as an HRESULT. This HRESULT is translated into an exception for #import (_com_error exception class) and MFC OLE (COleDispatchException class). With COM programming, you must review the returned HRESULT of each method to ensure that you have not encountered an error condition.

Exception handling within Visual C++ is recommended for all three implementations, although exceptions are far less likely to occur with native COM programming. Your application could raise two types of exceptions: Win32 and C++ exceptions. The ADO Rosetta Stone samples use the EXCEPTEX sample (available in the Visual C++ Knowledge Base) to convert Win32 structured exceptions into C++ exceptions and catch them with try/catch syntax. Whether you use #import, MFC OLE, or native COM programming, your application should have robust and comprehensive exception handling.

The modified version of the EXCEPTEX sample has been included in the LOG.H and LOG.CPP files. For help catching C++ and Win32 structured exceptions, see the EXCEPTEX Knowledge Base article available from http://support.microsoft.com/support. Listing 25 gives an example of what it takes to ensure you have robust exception handling. This code is a small excerpt of both the EXCEPTEX sample and the LOG files provided with the ADO Rosetta Stone samples for Visual C++, and provides working exception handling code you could add to your application.

Listing 25: Catching C++ and Win32 Exceptions

#include <eh.h>            // Required for Win32 Exception Handling
#include <stdio.h>         // Optional, specific to this sample
#include <afxwin.h>        // Required for MFC Exception Handling
#include <afxdisp.h>       // Required for MFC Exception Handling
#import  <msado15.dll> rename("EOF", "adoEOF")

// Class for Win32 Structured Exception Handling
class SEH_Exception {
private:
   unsigned int m_uSECode;
public:
   SEH_Exception(unsigned int uSECode) : m_uSECode(uSECode) {}
   SEH_Exception() {}
   ~SEH_Exception() {}
   unsigned int getSeHNumber() { return m_uSECode; }
};

// Raise Win32 Exception within a C++ Class SEH_Exception
static void MappingSEHtoCPPExceptions
(
   unsigned int uExceptionCode,
   _EXCEPTION_POINTERS*
)
{
   throw SEH_Exception( uExceptionCode );
}

// Sets up mapping between Win32 SEH & C++ Exception Handling
void LogEnable()
{
   _set_se_translator(MappingSEHtoCPPExceptions);
}

void main( void )
{
   try
   {
      ADODB::_ConnectionPtr  Conn1 = NULL;
      Conn1.CreateInstance( __uuidof( ADODB::Connection ) );
      LogEnable();

      // To see each of the catch blocks work, comment out all but
      // one of the following 4 lines, each of which throws an
      // exception.

      // Bogus DataSource Name, raises _com_error
      Conn1->Open( L"", L"", L"", -1 );

      // Explicitly raise MFC Exception
      AfxThrowMemoryException();

      // Explicitly raise SEH Exception
      RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);

      // Explicitly raise exception of a type not caught below
      // i.e. to the catch handlers, an "Unknown exception"
      throw 0L;   // Raises exception of type long

   }
   // Catch Blocks -- All of which are a subset of the
   //                 Exception Handling demonstrated in LOG.CPP
   //                 in the C++ Rosetta Stone Samples
   catch( CException *e )
   {
      CString         strName;
      CRuntimeClass   *pClass = e->GetRuntimeClass();

      printf( "MFC Exception(%s) thrown\n",
            (LPCTSTR) pClass->m_lpszClassName );
   }
   catch( _com_error &e )
   {
      printf( "#import encountered failed HRESULT\n" );
      printf( "\tCode = %08lx\n",      e.Error());
   }
   catch( SEH_Exception &e )
   {
      printf( "Win32 Structured Exception Raised\n" );
      printf( "\t Error Code %08lx\n", e.getSeHNumber() );
   }
   catch(...)
   {
      printf( "Caught an exception of unknown type\n" );
   }

   ::MessageBox( NULL, "Success!", "", MB_OK );
}

COleDispatchDriver::m_lpDispatch and Unrecoverable Exceptions

One weakness of the MFC OLE implementation for an Automation Server is that it is possible to generate an exception from which there is no recovery. That is, if you do not instantiate an actual ADO object, you can still invoke the methods of the helper class on that object. As long as the method is not returning another object, you will receive TRACE messages telling you, in a debug build, that you have not instantiated the object. Consider, though, calling Connection.Execute, which returns a Recordset object. Listing 26 generates an exception which triggers exceptions within exceptions, and there is no way to catch these. Before running this code sample, though, you should save your work in any other open applications (especially if running it in debug mode on Windows 95).

Listing 26: The Unrecoverable Exception via MFC OLE

#include <eh.h>         // Required for Win32 Exception Handling
#include <stdio.h>      // Optional, specific to this sample
#include <afxwin.h>     // Required for MFC Exception Handling
#include <afxdisp.h>    // Required for MFC Exception Handling
#import  <msado15.dll> rename("EOF", "adoEOF")
...
// Class for Win32 Structured Exception Handling
class SEH_Exception {
private:
   unsigned int m_uSECode;
public:
   SEH_Exception(unsigned int uSECode) : m_uSECode(uSECode) {}
   SEH_Exception() {}
   ~SEH_Exception() {}
   unsigned int getSeHNumber() { return m_uSECode; }
};

// Raise Win32 Exception within a C++ Class SEH_Exception
static void MappingSEHtoCPPExceptions
(
   unsigned int uExceptionCode,
   _EXCEPTION_POINTERS*
)
{
   throw SEH_Exception( uExceptionCode );
}

// Sets up mapping between Win32 SEH & C++ Exception Handling
void LogEnable()
{
   _set_se_translator(MappingSEHtoCPPExceptions);
}

void main( void )
{
   try
   {
      ADODB::_ConnectionPtr  Conn1 = NULL;
      Conn1.CreateInstance( __uuidof( ADODB::Connection ) );
      LogEnable();

      // To see each of the catch blocks work, comment out all but
      // one of the following 4 lines, each of which throws an
      // exception.

      // Bogus DataSource Name, raises _com_error
      Conn1->Open( L"", L"", L"", -1 );

      // Explicitly raise MFC Exception
      AfxThrowMemoryException();

      // Explicitly raise SEH Excetption
      RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);

      // Explicitly raise exception of a type not caught below
      // i.e. to the catch handlers, an "Unknown exception"
      throw 0L;   // Raises exception of type long

   }
   // Catch Blocks -- All of which are a subset of the
   //                 Exception Handling demonstrated in LOG.CPP
   //                in the C++ Rosetta Stone Samples
   catch( CException *e )
   {
      CString         strName;
      CRuntimeClass   *pClass = e->GetRuntimeClass();

      printf( "MFC Exception(%s) thrown\n",
            (LPCTSTR) pClass->m_lpszClassName );
   }
   catch( _com_error &e )
   {
      printf( "#import encountered failed HRESULT\n" );
      printf( "\tCode = %08lx\n",      e.Error());
   }
   catch( SEH_Exception &e )
   {
      printf( "Win32 Structured Exception Raised\n" );
      printf( "\t Error Code %08lx\n", e.getSeHNumber() );
   }
   catch(...)
   {
      printf( "Caught an exception of unknown type\n" );
   }

   ::MessageBox( NULL, "Success!", "", MB_OK );
}

This demonstrates a key point of MFC OLE (and #import). While this example instantiated the variable that wraps an ADO object, you also need to instantiate an instance of that object in order to use the variable successfully. In this case, this step is not performed with the Connection object. Therefore successive calls on ADO methods of the Connection Object do not execute, and MFC generates a TRACE message (visible only in debug builds) for each attempt to reference a property or method of that object.

Warning: attempt to call Invoke with NULL m_lpDispatch!

However no COleDispatchException is raised, so unless you specifically check the Conn1.m_lpDispatch variable to see if it is NULL, your code continues executing until it reaches the Execute method. At that point, Execute returns an invalid return value, which Rs1 assumes is a valid IDispatch pointer. Rs1 accepts this invalid Idispatch pointer through its constructor, and MFC, in the process of trying to instantiate Rs1, generates exceptions within exceptions which cannot be caught by the try/catch syntax.

In this one instance, you could modify the code generated by the ClassWizard to initialize the IDispatch pointer returned by CreateParameter as shown in Listing 27.

Listing 27: Overriding _Connection::Execute to Prevent Unrecoverable Exception

LPDISPATCH _Connection::Execute(LPCTSTR CommandText,
                                 VARIANT* RecordsAffected, long Options)
{
   LPDISPATCH result = NULL;   // Initialize pointer to prevent crash
   static BYTE parms[] =
      VTS_BSTR VTS_PVARIANT VTS_I4;
   InvokeHelper(0x6, DISPATCH_METHOD, VT_DISPATCH, (void*)&result, parms,
      CommandText, RecordsAffected, Options);
   return result;
}

For robustness, you should implement this initialization in every method of every class derived from COleDispatchDriver. This treats the symptom, not the problem, which is the failure to correctly initialize the Connection object in the first place. (Although an argument could be made for MFC OLE to raise a COleException to make the problem more visible when the code attempts to reference an uninstantiated object, rather than just returning a TRACE message).

C++ and Win32 exception handling have two advantages. They allow you to find catastrophic mistakes during development, such as not initializing the Connection object, and to prevent them from going out to your customer. The other advantage is ensuring that your application can recover gracefully if it encounters a catastrophic condition at runtime. Exception handling is not a universal cure-all, and try/catch cannot replace robust coding. With MFC OLE, you cannot be guaranteed that you will be able to use exception handling under both scenarios.

Java

As with Visual C++, you use the try/catch syntax to catch exceptions with Java. At a minimum, you must catch the following exceptions:

com.ms.com.ComFailException
Exception

The spirit of the ExceptEx sample for Visual C++ is maintained for the ADOVJ sample.

Listing 28: Error Handling with ADO with Java Type Library Wizard

public static void AdoErrorEx( com.ms.wfc.data.Connection Conn1 )
{
   // Local Error Objects
   com.ms.wfc.data.Errors     Errs1 = null;
   com.ms.wfc.data.Error      Err1  = null;

   long      nCount;

   if ( Conn1 == null )
      return;

   Errs1  = Conn1.getErrors();
   nCount = Errs1.getCount();
   for( long i = 0; i < nCount; i++ )
   {
      Err1 = Errs1.getItem( (int) i );
      System.out.println( "   Number " + Err1.getNumber() );
      System.out.println( "      Description = " + Err1.getDescription()  );
      if ( Err1 != null ) Err1 = null;
   }
   if ( Errs1 != null ) Errs1 = null;
}

public static void main( String args[])
{
   com.ms.wfc.data.Connection Conn1      = new com.ms.wfc.data.Connection();

   try
   {
      Conn1.open( "", "", "", -1  );
   }
   // Catch Blocks -- These catch blocks are a subset of the
   //                 Exception Handling demonstrated in LogEx.Java &
   //                 AdoUtils.java in the Java Rosetta Stone Samples
   catch (com.ms.com.ComFailException e)
   {
      // Test if Connection.Errors Collection is non-empty
      AdoErrorEx( Conn1 );

      // Crack open ComFailure class that wraps COM Exceptions
      System.out.println( "ComFailException Class Raised:" );
      System.out.println( "Exception Class = " + e.getClass()   );
      System.out.println( "Error Message   = " + e.getMessage() );
      System.out.println( "Failed HRESULT  = " + e.getHResult() );
   }
   catch(Exception e)
   {
      // Test if Connection.Errors Collection is non-empty
      AdoErrorEx( Conn1 );

      // Crack open Java Exception class
      System.out.println( "Exception Class Raised:" );
      System.out.println( "Exception Class = " + e.getClass()   );
      System.out.println( "Error Message   = " + e.getMessage() );
      }
}

Scripting

VBScript

VBScript does not offer any language support for trapping errors. You can check the Err object and use On Error Resume Next, but it is not possible to set up any kind of error handler such as is used with VBA in the example. Therefore, you must thoroughly test your VBScript application during development because problems will not be caught during deployment.

Listing 29: Error Handling with ADO in VBScript

<HTML>
<HEAD>
   <SCRIPT LANGUAGE="VBScript">
<!--
Sub Procedure1()
   on error resume next

   badcommand

   if len(err.description) > 1 then
      msgbox "error is: " & err.description
   end if

end sub
-->
   </SCRIPT>
<TITLE> Page1 </TITLE>
</HEAD>
<BODY>
   <FORM NAME="form1">
      <INPUT LANGUAGE="VBScript" TYPE=button VALUE="button1" ONCLICK="call Procedure1()">
   </FORM>
</BODY>
</HTML>

JScript

Error handling is not straightforward because JScript does not support try/catch syntax. In fact there is no JScript-specific support for server-side error handling. You can use HTML tags to set up a client-side error handler, as shown in Listing 30.

Listing 30: Error Handling with ADO in JScript

<HTML>
<HEAD>
<SCRIPT LANGUAGE="JScript"
FOR="window"
EVENT="onerror(msg,url,lineno)">
<!--
   alert("window.onerror\n\n" +
      "Error: " + msg + "\n" +
      "URL:  " + url + "\n" +
      "Line:  " + lineno);
   return true; // Tell the browser not to report the error itself
//-->
   </SCRIPT>
   <SCRIPT LANGUAGE="JScript"
         FOR="button1"
         EVENT="onclick()">
   <!--
      badcommand();
   //-->
   </SCRIPT>
</HEAD>
<BODY>
   <INPUT TYPE=button VALUE="Click for error" NAME=button1>
</BODY>
</HTML>


Part Three: Advanced Discussion and Related Information

A Closer Look at #import

ADO is an excellent way to learn about COM, and #import is an excellent way to utilize COM. This section describes how to implement #import in COM and how it affects an ADO application. Even if you are not a developer, the discussion describes what your development environment provides invisibly. Most of the text in this section was borrowed from the "Mastering COM Development with Visual C++" course offered by Microsoft Authorized Training and Education Centers.

#import and COM

The #import directive instructs the compiler to read a specified type library and generate two header files that are automatically included in your Visual C++ source file. Both header files are placed in the output directory and given the same timestamp as the type library. The type library header file has the name <typelibname>.TLH and contains a Visual C++ equivalent of the original type library. The type library implementation file has the name <typelibname>.TLI and contains implementations of any wrapper functions.

The type library header (.TLH) file contains the definitions constructed from the type library items. It is divided into the following sections:

  • The #include statement for COMDEF.H, which defines the macros used in the header.

  • Forward references and typedefs, structure declarations for interface ids, and class names.

  • Smart pointer typedef declarations, using the _COM_SMARTPTR_TYPEDEF macro to create typedefs of specialized _com_ptr_t template classes.

  • Type library items, class definitions, and other items generated from the specified type library.

  • Named GUID constants initializations, if the named_guids attribute was specified.

  • An #include statement for the type library implementation (.TLI) file.

  • The type library implementation (.TLI) file contains the inline implementations of the wrapper functions.

The functions that the compiler generates can be divided into two types: high-level error handling functions and low-level direct functions. The high-level wrapper functions invoke the low-level method, perform error checking of the returned HRESULT, and throw a _com_error C++ exception if an error occurs. The low-level functions call the method directly and return an HRESULT. High-level functions are generated by default, but you can change the default by specifying the raw_interfaces_only attribute in your #import statement.

Using the Type Library with the #import Directive

When you build the COM object server, the first utility that runs is the MIDL compiler. The MIDL compiler creates a type library.

To use the #import directive to import a type library into ADO 1.5, use the following example code:

#import <msado15.h> rename ( "EOF", "adoEOF" )

In MFC client applications, you can place the #import directive in the STDAFX.H file after the #include <afxwin.h> directive.

The #import directive creates two header files that reconstruct the type library contents in C++ source code: a primary and a secondary header file.

The primary header file is similar to the one produced by the MIDL compiler, but it also contains additional compiler-generated code and data. This header file has the same base name as the type library, with a .TLH extension.

If the type library exposes custom interfaces and methods, their wrappers are placed in the secondary header file. This header file has the same base name as the type library, with a .TLI extension. The secondary header file contains the implementations for compiler-generated member functions, and is included (#include) in the primary header file.

Both header files are placed in the output directory of the project. The files are read and compiled as if the primary header file was named by a #include directive.

You can import the type library (.TLB file), the DLL, or the executable file with the #import directive. It may be easier to import the .TLB file because it stays in the same place for both Debug and Release builds.

For more information about the contents of the primary and secondary header files, see the online help in Microsoft Visual Studio.

A number of support classes that are used in the implementation of compiler COM support are also available for use in your client application.

_com_error

This class represents a C++ exception thrown by a high-level wrapper function or COM support class. It encapsulates the HRESULT and optional IErrorInfo object.

_com_ptr_t

This template class is a smart-pointer implementation that encapsulates interface pointers and eliminates the need to call AddRef, Release, and QueryInterface functions. Compiler COM support uses the _COM_SMARTPTR_TYPEDEF (IKnown, _uuidof(IKnown) macro when generating interface pointers from a type library; this macro expands to the following:

typedef _com_ptr_t<_com_IIID<IKnown, uuidof(IKnown)> > IKnownPtr;

This means that you no longer must remember to call Release. When the smart pointer goes out of scope, the destructor handles the release for you automatically. You can arrange a resource cleanup whenever you like by simply setting the pointer to NULL.

_variant_t

This class is a wrapper for the Automation VARIANT data type. It provides memory allocation and deallocation, and calls SysAllocString and SysFreeString when appropriate.

_bstr_t

This class is a wrapper for the Automation BSTR data type. It provides memory allocation and deallocation, and calls VariantInit and VariantClear when appropriate.

Dissecting the .TLH and .TLI Files Generated by #import

For any given type library, #import generates two files whose name matches the name of the file that contains the type library, and with the extensions .TLH and TLI respectively. The .TLH (or type library header) contains forward references on GUIDs in the type library, smart pointer declarations and other type library items. The .TLI (or type library implementation) file contains wrappers for invoking methods and accessing properties for each object in the type library.

Forward references are created to type a GUID to a particular object within the type library. Consider the following forward references generated from ADO 1.5 for various Connection objects defined in the ADO type library.

struct __declspec(uuid("00000515-0000-0010-8000-00aa006d2ea4"))
/* dual interface */ _Connection;

struct __declspec(uuid("00000516-0000-0010-8000-00aa006d2ea4"))
/* interface */ ADOConnectionConstruction;
...
struct /* coclass */ Connection;
...

Not all objects have multiple implementations, as this is specific to the connection object. For ADO, most interfaces are defined as dual interfaces and are what you typically use. In this case, ADOConnectionConstruction is an internal interface used by ADO to bind to an OLE DB Provider, and should not ever be used directly. It is unsupported, undocumented, and will remain so in the future. This raises one of the more interesting points about #import or reading the type library in general. Not all of the objects exposed are useful to ADO developers. Some are artifacts for backward compatibility, some are there of necessity in order to actually implement ADO itself (such as ADOConnectionConstruction), and some are of limited use to ADO developers (such as Unspecified enumerated values). In general, though, the documented portions of the type library are what you should be using, and hidden or undocumented elements of the type library are usually undocumented for a reason. There is no implicit advantage for using an undocumented element of the type library, and anything not documented is unsupported by Microsoft Product Support Services.

Following these forward references, you see the declarations of smart pointers for each object.

_COM_SMARTPTR_TYPEDEF(_Connection, __uuidof(_Connection));
...

Note that smart pointers are generated only on the dual interface and coclass Connection objects. Typically, though, you use only the smart pointer created on the dual interface objects for ADO. To invoke the Connection object you would use the following code to declare an instance of the smart pointer and create an actual ADO Connection object.

...
_ConnectionPtr   conn1;
Conn1.CreateInstance( __uuidof( Connection ) );

The __uuidof clause refers back to the forward declaration for the Connection object defined earlier in order to provide the correct GUID.

Other type library items are provided within the .TLH file, such as enumerated types defined within the type library. This is especially important for the Visual C++ developer. However, it can also expose items in the type library that you were not meant to see. Consider the following definition for CommandTypeEnum.

enum CommandTypeEnum
{
   adCmdUnspecified = -1,
   adCmdUnknown = 8,
   adCmdText = 1,
   adCmdTable = 2,
   adCmdStoredProc = 4,
   adCmdFile = 256,
   adCmdTableDirect = 512
};

Following the declarations for enumerated types exposed by the type library, you see the declarations of the classes wrapped by the smart pointers defined previously. Below is an excerpt of the _Connection object's smart pointer class.

struct __declspec(uuid("00000515-0000-0010-8000-00aa006d2ea4"))
_Connection : _ADO
{
   //
   // Property data
   //

   __declspec(property(get=GetConnectionString,put=PutConnectionString))
   _bstr_t ConnectionString;
   ...

   //
   // Wrapper methods for error-handling
   //

   _bstr_t GetConnectionString ( );
   void PutConnectionString (
      _bstr_t pbstr );

   //
   // Raw methods provided by interface
   //

   virtual HRESULT __stdcall get_ConnectionString (
      BSTR * pbstr ) = 0;
   virtual HRESULT __stdcall put_ConnectionString (
      BSTR pbstr ) = 0;
   ...

};

You can use various arguments with the #import statement to control whether you get the wrappers, raw interfaces, or by default, both. In this case the declspec( property( get=GetConnectionString, put=PutConnectionString ) ) defines the methods for getting and putting the value of a property. In this case it utilizes the GetConnectionString() and PutConnectionString() methods, which themselves are wrappers around the raw interface methods, as shown in this excerpt from the .TLH file. You can disable these helpers, but they allow you to assign and retrieve the value of the ConnectionString property without actually invoking a method.

inline _bstr_t _Connection::GetConnectionString ( ) {
   BSTR _result;
   HRESULT _hr = get_ConnectionString(&_result);
   if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
   return _bstr_t(_result, false);
}

inline void _Connection::PutConnectionString ( _bstr_t pbstr ) {
   HRESULT _hr = put_ConnectionString(pbstr);
   if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
}

These wrapper functions are typical of every implementation method found in the .TLI file. That is, if a failed HRESULT is returned by the raw interface method, you need to trap the _com_error exception that #import raises. Within that exception is the value of the failed HRESULT as well as other information #import can collect about the description and origin of this error. You should still check the ADO Errors collection for additional information. If the provider raises more than one error, only the first error is wrapped in an exception. If ADO generates the error, then the _com_error exception is your only way to trap the information ADO tries to give to you when it encounters an error.

   _ConnectionPtr   conn1;
   _bstr_t          bstrConnect(L"DSN=AdoDemo;UID=admin;PWD=sa" );

   conn1.CreateInstance( __uuidof( Connection ) );
   conn1->ConnectionString = bstrConnect;

Single vs. Double Quote in SQL Statements

If you have code that uses SQL syntax to make changes in a datastore, and you migrate that code from DAO to ADO, you might have to change some of the syntax, particularly double quote characters. With DAO, the Microsoft® Jet Database Engine expects double quotes in SQL syntax. With ADO, the Access ODBC Driver expects single quotes in SQL syntax. This example works within DAO (but not within ADO) to insert a new record into a table:

   INSERT INTO Authors ( Au_ID, Author ) VALUES (  54, "Record # 54" )

You must change the double quotes to single quotes for the example to execute successfully in ADO, as shown here:

   INSERT INTO Authors ( Au_ID, Author ) VALUES (  54, 'Record # 54' )

Ironically, the OLE DB Provider for the Microsoft Jet Engine can handle either form of syntax, as shown in the following code snippet.

Dim cnn2 As New ADODB.Connection
cnn2.provider = "Microsoft.Jet.OLEDB.3.51"
cnn2.Open "C:\_data\adodemo.mdb", "admin", ""
cnn2.Execute _
   "INSERT INTO Authors ( Au_ID, Author ) VALUES (  55, 'Record 55' )"
cnn2.Execute _
   "INSERT INTO Authors ( Au_ID, Author ) VALUES (  56, ""Record 56"" )"
cnn2.Close

Conclusion

One of the misconceptions about ADO development in Visual C++ or Java is that it is inherently more difficult than in VBA. However, the difficulty lies not so much with ADO as with COM. Specifically, you need to consider how easily the language or implementation you choose exposes and lets you manipulate COM objects. VBA is easier to use with COM objects, but this does not mean you have to write 50 lines of code in Visual C++ for every two lines in VBA.

To give Visual C++ and Java developers some of the advantages that VBA developers experience in minimal code size, several tools are available to help create ADO applications in languages other than VBA. For Visual C++ developers, this includes the MFC OLE classes and #import. For Java developers, the Java Type Library Wizard and the Microsoft SDK for Java help eliminate much of the difficulty of creating ADO applications.

Bibliography

ADO and RDS Knowledge Base articles are concentrated within a single product rollup, "ActiveX Data Objects," so as to prevent ADO expertise and technical content from fracturing between the dozen or so products that use ADO. In addition, "Programmer's Guide" articles are available and scattered within various product rollups so that you have a roadmap pointing you to additional content. ADO articles often have sample code in VBA, Visual C++, and Java, and can help you translate any articles which do not have sample code written in all three languages.

This white paper represents the "big picture" of what many of the Knowledge Base articles listed below present in detail.

Q166112: PRB: Conflict with EOF When Using #import with ADO

Q167802: SAMPLE: EXCEPTEX Traps MFC and Win32 Structured Exceptions

Q172391: FILE: GetRows.exe Demonstrates #import with ADO and GetRows()

Q172392: INFO: Programmer's Guide to Using ADO in Visual C++

Q172403: FILE: Adovb.exe Demonstrates How to Use ADO with Visual Basic

Q172441: INFO: Programmer's Guide to Using ADO in Visual J++

Q172589: INFO: Programmer's Guide to Using ADO in Visual InterDev

Q172927: INFO: Programmer's Guide to Using ADO in Active Server Pages

Q173645: BUG: Access Violation in Msdaer.dll with _com_error Exceptions

Q173647: INFO: Programmer's Guide to Using ADO in Visual Basic

Q174565: FILE: Adovc.exe Demonstrates How to Use ADO with Visual C++

Q181733: FILE: Adovcbtd.exe #import Using UpdateBatch and CancelBatch

Q182067: INFO: Programmer's Guide to Using RDS in Visual Basic

Q182389: FILE: Adovcbm.exe ADO 1.5 with #import and Getrows/Bookmarks

Q182442: FILE: Adomts.exe Shows Using ADO w/ an MTS component via DCOM

Q182443: INFO: Programmer's Guide to Using RDS in Visual Basic

Q182782: FILE: Adovj.exe Demonstrates How to Use ADO with Visual J++

Q183609: FILE: Rdsvb.exe Demonstrates How to Use RDS with Visual Basic

Q183616: HOWTO: Trap JScript Errors on a Web Page

Q184381: INFO: Programmer's Guide to Using MDAC in Visual Basic

Q184382: INFO: Programmer's Guide to Using RDS in Visual C++

Q184383: INFO: Programmer's Guide to Using RDS in Visual J++

Q184493: INFO: Programmer's Guide to Using Index Server Provider in Visual C++

Q184736: INFO: Troubleshooting Guide for 80004005 & Other Error Messages

Q184736: INFO: Programmer's Guide to Using Index Server Provider in Visual Basic

Q185033: FILE: Adoacc.exe Demonstrates Using ADO with Microsoft® Access 97

Other Sources of Information

Dale Rogerson, Inside COM. Microsoft Press, 1997

Automation Programmer's Reference. Microsoft Press, 1997

The Data Access SDK can be obtained from http://www.microsoft.com/data/.

The OLE DB 1.5 SDK can be obtained from http://www.microsoft.com/data/oledb/.


ADO for Windows Foundation Classes Overview

The new Microsoft® ActiveX® Data Objects for Windows Foundation Classes (ADO/WFC) components provide a robust, powerful set of data access services. Developers can use these components to write Java-based applications and components for Microsoft® Win32® platforms and premier hosting environments such as Microsoft® Internet Explorer 4.0. The following topics provide an overview of ADO/WFC and demonstrate how to use the components:

  • Architecture and Component Model

  • Programming Model

  • Programming Samples

Use these programming examples as building blocks. By combining several of these simple scenarios, you can create more complex application behaviors. These samples discuss various topics, including asynchronous execution, remote business object invocation, transactional data access, and metadata examination.

For updated information on the continuing evolution of ADO/WFC, visit the Microsoft Data Web site at http://www.microsoft.com/data.


Architecture and Component Model

The ADO/WFC components leverage the rich functionality of Win32 platforms, and are designed for optimal performance with the Microsoft virtual machine (Microsoft VM) technology. These components are part of the Microsoft® Data Access Components.

The following table summarizes the major ADO/WFC components:

Component | Description
Connection | Provides basic connectivity to data sources and application servers.
Recordset | A data cursoring abstraction. Provides rich navigational semantics, as well as disconnected and batch mode operational modes. Intrinsically marshalable across process/machine boundaries (basic unit of “transport” for tabular data between business objects and client application contexts).
Command | A parameter-varying command execution component.
Field (Fields Collection) | Handles row metadata and values. Used to define the “shape” and dimension of Recordset objects.
AdoProperty (AdoProperties Collection) | A property container for component-specific properties. All ADO components have associated (and in some cases unique) property sets.
Parameter (Parameters Collection) | A value descriptor for objects utilized by Command components.
ObjectProxy | A surrogate for a remote business object.
DataSpace | A business object “factory” component.
DataBinder (and DataBinding) | A data binding and coordination agent for forms in an ADO-deployment context. (DataBinding is a helper class that associates a visual component’s bound fields to Recordset result fields.)
ConnectionEventHandler | A dispatch mechanism for Connection events to registered event listeners.
RecordsetEventHandler | A dispatch mechanism for Recordset events to registered event listeners.
CommandEventHandler | A dispatch mechanism for Command events to registered event listeners.
ConnectionEvent | An event descriptor specific for Connection events.
RecordsetEvent | An event descriptor specific for Recordset events.
CommandEvent | An event descriptor specific for Command events.
AdoException (Errors Collection) | A basic error exception class for reporting anomalous conditions in the ADO execution environment.


Programming Model

ADO/WFC supports two different methodologies for application development. The first method is the common client/server (or occasionally called “API”) approach. In this approach, the ADO/WFC classes are used much like JDBC, ADO/Win32, or DAO/RDO. The programmer creates applications by writing explicit code. This approach is a familiar paradigm and ADO/WFC offers a rich model for distributed data programming. The programming samples provide several code examples of using ADO/WFC, from simple connectivity to distributed object invocation with transactional safeguards.

The second approach relates to rapid application development. By implementing the IComponent interface, and the new event model for Java components, ADO/WFC presents a convenient set of interfaces and properties for “visual programming.” The ability to support live data hosting in design environments, component “pluggability,” and automatic data binding through well-defined interfaces makes ADO/WFC a powerful tool for Java developers working on Win32 platforms.


Programming Samples

ADO/WFC provides a sophisticated array of data access services, allowing the Microsoft® Visual J++® developer to create rich, data-centric applications. To help you get started using ADO/WFC, this section provides the following set of programming samples:

Sample | Description | Components Used
Framework Code | Provides a runnable framework in which to copy the code examples. |
Connection Sample | Connects to a data source and executes a simple query that results in a forward-only, server cursor. | Connection
Recordset Sample | Opens a disconnected Recordset and populates it with the results of a data source query. Performs several Recordset-oriented operations, such as adding, deleting, updating, and canceling local updates. Note that this is a large code sample. You should thoroughly understand the Connection object before trying this sample. | Connection, Recordset, AdoProperty, AdoProperties (collection)
Command Sample | Uses a Command object to execute a query with parameters. This is a performance optimization mechanism for highly repetitive operations that vary only in the value of the parameters to the operation. | Connection, Recordset, Command, Parameter
Asynchronous Command Execution Sample | Uses asynchronous mode to allow background population of a cursor. | Connection, Recordset, ConnectionEventHandler
Transaction Sample | Uses transaction and isolation levels to control data change visibility in applications. Ensures changes are committed or aborted. | Connection, Recordset, Transaction
DataSpace Sample | Invokes business objects on an application server using the Remote Data Services (RDS) and Internet Information Server (IIS). Marshals the Recordset result back using SOAP (Simple Object Access Protocol). | Connection, Recordset, ObjectProxy, DataSpace
Event Handling Sample | Hooks up event handlers to process changes in Recordsets, allowing all interested “listeners” to track changes. | Connection, Recordset, RecordsetEventHandler, RecordsetEvent
Data Navigation Sample | Opens a Recordset and navigates through its cached data. Examines the metadata of the Recordset by iterating through the Fields collection of the Recordset. | Connection, Recordset, Field, Fields (collection)

Note You should begin with the Framework Code sample, which shows how to set up your application. You can then use the code in the other samples to add the desired functionality.


Framework Code

In order to use the programming samples that are provided, you need a framework in which to plug in the code. Rather than repeat the same framework code for each sample, the necessary code is included here.

Note These programming samples assume that you are familiar with the basic concepts of Visual J++, such as creating a project, adding a new class to the project, and compiling your code. If you are unfamiliar with these concepts, use the online help to view the entries for these concepts.

The framework code consists of three main sections:

  • The necessary import declaration for using the ADO/WFC classes.

  • The instantiation of the Connection object.

  • The releasing of data resources when the application has finished.

Note It is recommended that you close and release the ADO/WFC components before your application terminates. Otherwise, the VM may end up with large cached data sets on its heap, which slows garbage collection and unnecessarily increases the size of the heap. Although none of the samples provided here actually require these measures, developing these habits will help you as your data applications with ADO/WFC become more sophisticated.

Use the following framework code to get your application started. This code assumes that the run method is invoked sometime after the object is instantiated. (It does not have to be invoked from the main method, as shown in this example.)

import com.ms.ado.*;

public class DemoFramework
{
   public boolean run()
   {
      Connection c = new Connection();
      c.setConnectionString ("dsn=aDSNName;pwd=aName;pwd=aPWD;database=aDatabase");
      c.open();
      c.setDefaultDatabase("aSpecificDatabase");

      System.out.println ("Connection string = " + c.getConnectionString());
      System.out.println ("Version = " + c.getVersion());
      System.out.println ("Provider = " + c.getProvider());
      System.out.println ("State = " + c.getState());
      System.out.println ("Attributes = " + c.getAttributes());

      // Most of the code samples will be inserted here, after
      // the connection has been established successfully.

      c.close();
      return true;

   }

   public static void main(String args[])
   {
      new DemoFramework().run();
   }
}

The first line of the framework code is an import statement, which makes the ADO/WFC components available to the class. The next step is to establish a connection to a data source. Connections can be either durable or transitory. Durable connections actually maintain a link to the data source, across potentially several operations. Most client/server data applications are implemented with durable connections. The Internet changes this, however, with its requirements for not maintaining long-running sessions with clients. Therefore, transitory connections release any link to the data source after each operation, allowing the data source to service other requests. When you specify the ConnectionString property of the Connection object, a transitory connection can later reconnect to the data source.

The Connection object is created in the run method, and the ConnectionString property is set to a specific system data source name (DSN) through the call to setConnectionString. (You should replace the DSN and other parameters with values that apply to your site. The names used in this sample are simply placeholders.)

Next, the connection is opened through the open method. By default, the open method establishes a durable connection to the data source. If successful, the application can proceed to execute queries against the source’s data. The diagnostic lines that follow simply print out some common information about the connection: what sort of driver is being used, the attributes of the connection, and the provider. The default database is also set for the connection through the call to setDefaultDatabase. If an update or query fails to specify a specific database when it executes, the connection will assume the command or query should be performed against the default database. This is a convenience function only.

The framework then includes a placeholder comment that indicates where you can insert code from the other samples. Remember to close durable connections when you are through with them. This has the effect of releasing certain resources that will improve the performance of your own code and allow for quicker heap reclamation. Default garbage collection can be sub-optimal when large chunks of memory are in question, such as in the case of a large result set (100K rows). This is critical when the ADO/WFC classes are being used on middle-tier application servers inside business objects.

Finally, the framework code is initiated within the main method of the class. This is not a mandatory construct. You may want to set up your own class to initiate a connection when a certain event is triggered inside your application by an end user, such as a button click event. You can alter the code to suit your application requirements.

Compile the class and verify that you can connect to a data source at your site. The diagnostic messages will confirm whether you have established a connection. If desired, you may then remove the System.out lines from the framework code.


Connection Sample

This sample is a simple extension of the framework code. Essentially, the only new element introduced is a query execution, which results in a Recordset. You can iterate through each element in the Recordset and move to the very start or end of the Recordset. To programmers familiar with ANSI cursors, the Recordset may be thought of as an elaboration of the cursor concept. It also includes scrolling and remoting (moving the entire result set to another process rather than one row at a time).

The code below only includes the run method, which you can insert into the framework code:

public boolean run()
{
   Connection c = new Connection();
   c.setConnectionString ("dsn=aDSNName;pwd=aName;pwd=aPWD;database=aDatabase");
   c.setCommandTimeout (10);
   c.setConnectionTimeout (10);
   c.setCursorLocation (AdoEnums.adUseClientBatch);

   c.open();
   c.setDefaultDatabase ("aSpecificDatabase");
   System.out.println ("Connection string = " + c.getConnectionString());

   // Verify that the propertiea on the connection
   // were set correctly. If not, return a fail condition.
   if (c.getCommandTimeout() != 10)
      return false;

   if (c.getConnectionTimeout() != 10)
      return false;

   if (c.getCursorLocation() != AdoEnums.adUseClientBatch)
      return false;

   if (c.getMode() != AdoEnums.adModeUnknown)
      return false;

   // Create a new Recordset to contain the results
   // of the SQL DML operation (select * ...).
   Recordset rs= c.execute ("select * from authors");
   rs.moveFirst();
   rs.moveLast();

   // Delete all rows from the authors table.
   c.executeUpdate ("delete from authors");

   // Close the Connection and Recordset.
   rs.close();
   c.close();
   return true;

}

This sample introduces a number of options that were not discussed in the framework code. ADO/WFC can operate in “default” mode, where the developer doesn’t have to set all of the options possible. This is a trade-off with functionality, since the default behavior of ADO/WFC is to create server-side, forward-only cursors. This means that scrolling is not supported by default. To get the convenience of scrolling cursors and locally cached data, rather than performing a network roundtrip for each row fetched, the following options are set in the code:

c.setCommandTimeout (10);
c.setConnectionTimeout (10);
c.setCursorLocation (AdoEnums.adUseClientBatch);

The CursorLocation property tells the ADO/WFC run time to create a client-side cursor and to operate in batch mode. The advantage of this type of cursor is increased navigational speed and the ability to make changes all at once. The other two Connection properties specify a timeout for the connection. This is typically used to ensure that an application doesn’t enter a long wait when a server resource is not available. In this example, the attempt to connect will be aborted if the server/source does not acknowledge the connection request within 10 seconds. The default setting for both of these timeout properties is indefinite; this means the application will go into a wait mode until connected or until the application process is terminated.

The line, c.executeUpdate("delete from authors"), provides the major piece of functionality in this sample. Here, the application uses the open connection to issue a SQL DML statement to delete all rows in the authors table. There are other execute operations that return Recordset objects. These execute operations are typically select statements or stored procedure calls that return one or more Recordsets as “results.” Since this is a delete operation, there is no result data to process (aside from potential error messages), and the executeUpdate method is employed. The Recordset class offers multiple options for controlling the nature of the “result.”

Try experimenting with a few different queries and alter the cursor type and location properties. Depending on your network and machine, you should see some performance differences. ADO/WFC provides the convenience of server cursors or the capabilities of “remoting” your data to the client, where navigation and data access operations can be performed, and the changes sent back to the server, in a "batch.” This greatly reduces the overhead of data marshaling.


Recordset Sample

This sample extends the framework code by making use of the Recordset, one of the core ADO/WFC classes. Recordsets can be opened much like a Connection, and can be used to execute data operations (such as add, remove, update) and to marshal tabular data to and from business objects.

// Establish a global variable to hold the open Recordset
// used in run(), createRecordset(), and useRecordset().
private Recordset rs;

public boolean run()
{
   boolean fSuccess = true;
   fSuccess = createRecordset();
   useRecordset ();
   rs.close();
   return fSuccess;
}

boolean createRecordset ()
{
   Connection c = new Connection();
   c.open ("server=aDSN; ", "aName", "aPWD");

   rs = new Recordset();
   rs.setActiveConnection ("dsn=aDSN;database=pubs");
   rs.setSource ("select * from authors");
   rs.setActiveConnection (c);

   rs.setCursorType (AdoEnums.adOpenStatic);
   rs.setCursorLocation (AdoEnums.adUseClientBatch);
   rs.setLockType (AdoEnums.adLockBatchOptimistic);
   rs.setCacheSize (10);
   rs.setMaxRecords (1000);
   rs.open();

   return true;
}

boolean useRecordset()
{
   // Extract all of the Recordset properties and
   // print out the <attribute, value>-tuples.
   AdoProperties props = rs.getProperties();
   int cProps = props.getCount();
   for( int iProp = 0; iProp < cProps; iProp++ )
   {
      AdoProperty p = props.getItem( iProp );
      System.out.println("[" + p.getName() + " = " + p.getString() + "]");
   }

   // Perform simple navigation and boundary checking
   // (BOF= beginning of file, EOF= end of file).
   rs.moveFirst();
   rs.movePrevious();
   if (! rs.getBOF())
      return false;

   rs.moveLast();
   rs.moveNext();
   if (! rs.getEOF())
      return false;

   int recordCount = rs.getRecordCount();
   System.out.println ("record count = " + recordCount);

   rs.addNew( new Object[] {"899-46-2036", "O'Maley", "Zoltar",
      "67 Seventh Av.", "Salt Lake City", new Boolean (true),
      "801 826-0752", "UT", "84152","409-56-7098"});

   rs.update();
   rs.delete(1);
   rs.addNew();
   rs.cancelUpdate();

   rs.moveFirst();
   rs.delete(1);
   rs.cancelBatch();

   rs.setMarshalOptions (AdoEnums.adMarshalModifiedOnly);
   rs.setPersistFormat (AdoEnums.adPersistADTG);

   if (rs.getMarshalOptions() != AdoEnums.adMarshalModifiedOnly)
      return false;
   if (rs.getPersistFormat() != AdoEnums.adPersistADTG)
      return false;

   rs.moveFirst();
   Object bmk = rs.getBookmark();
   rs.moveLast();
   rs.setBookmark(bmk);
   rs.movePrevious();
   if (! rs.getBOF())
      return false;

   rs.addNew();
   if (rs.getEditMode() != AdoEnums.adEditAdd)
      return false;
   rs.cancelUpdate();

   // Requery the result, overriding any local changes
   // and completely replacing the result.
   rs.requery();

   return true;
}

The createRecordset method creates a Recordset by opening a connection and executing a query against the data source referenced by the System DSN:

rs = new Recordset();
rs.setActiveConnection ("dsn=aDSN;database=pubs");
rs.setSource ("select * from authors");

The ActiveConnection property is used in much the same way that the ConnectionString property is utilized on the Connection object — to specify a data source to connect to. The Source property is an overloaded property that can either reference a SQL DDL/DML string or a Command object. In this case, a string is used to initialize the Recordset. Note that no data has been fetched yet, since the Recordset has not been opened. It is also possible to set the ActiveConnection property of a Recordset to refer to a Connection object instance, as in rs.setActiveConnection(c).

The remaining properties are used to specify aspects of the type of cursor required by the application:

rs.setCursorType (AdoEnums.adOpenStatic);
rs.setCursorLocation (AdoEnums.adUseClientBatch);
rs.setLockType (AdoEnums.adLockBatchOptimistic);
rs.setMaxRecords (1000);
rs.open();

Note that in the absence of explicit property settings, any Recordset created by an open method invocation will result in a static, forward-only, server cursor — the equivalent of a generic ANSI cursor. However, you may need a cursor that supports scrolling and batch behavior; you may need a “disconnected” cursor where you can apply changes locally before deciding whether to propagate those changes back to the data source. Applications can be written to capitalize on this local caching of data, which provides a better solution for Internet applications that cannot afford to keep server resources tied up. The particular property settings chosen above request a static, local cursor that does not keep any locks on server resources. Instead, it uses optimistic concurrency control when it tries to update the data source. The MaxRecords property specifies an upper bound on the amount of rows that should be returned. Finally, the Recordset is opened through the open method. If the connection information has been specified properly, the Recordset is populated with up to 1000 rows of data.

The useRecordset method begins with an iteration over the Recordset properties that were set in the createRecordset method. The properties are returned in a collection, AdoProperties, which is then traversed. Each attribute name of the Recordset is printed, followed by the value of the specific property. This type of information may be considered “component metadata” — information about the state of the component, rather than on what it contains.

AdoProperties props = rs.getProperties();
int cProps = props.getCount();
for( int iProp = 0; iProp < cProps; iProp++ )
{
   AdoProperty p = props.getItem( iProp );
   System.out.println("[" + p.getName() + " = " + p.getString() + "]");
}

The next block of code consists of simple navigation commands. If the Recordset has a scrollable cursor, it supports the following navigational methods: moveFirst, moveNext, movePrevious, and moveLast. Recordsets also provide a bookmark object so that a position may be bookmarked and navigated back to quickly and efficiently. This requires you to use code similar to the following:

rs.moveFirst();
Object bmk = rs.getBookmark();
rs.moveLast();
rs.setBookmark(bmk);
rs.movePrevious();

A bookmark has an “opaque” structure, but two bookmarks can be compared to determine if they reference the same location. This is useful to writers of visual components such as data grids and other complex data-bound components. In the code above, the bookmark is set for the current row pointed to by the Recordset. The current position is then changed to the final row in the set. Then, the bookmark is used to set the position back to where the Recordset was pointing before the position was moved to the final row. Finally, the Recordset is moved back from the new position, placing it before the first row in the set. Recordsets have two special “positions” or states: just before the first row and just after the last row. These states are called BOF and EOF, respectively, and are often used in iteration constructions to terminate navigation. Testing for the values of these boolean properties is quite common in ADO/WFC programming and familiar to programmers from DAO and RDO.

The next cluster of operations performed on the open, populated Recordset revolve around data manipulation, such as adding rows, deleting rows, accepting local updates, and canceling these local changes. The advantage of the local, disconnected cursor can be seen in such circumstances. The programmer has wider discretion to modify local, non-persistent data, rather than alter production data in the data source. This allows for a more efficient partitioning of application logic between the client, the application server (if utilized), and the data source.

rs.addNew( new Object[] {"899-46-2036", "MindFlayer", "Zortan",
   "67 Seventh Ave.","Salt Lake City", new Boolean (true),
   "801 826-0752", "UT", "84152", "409-56-7098"});

rs.update();
rs.delete(1);
rs.addNew();
rs.cancelUpdate();

New rows are added to the Recordset in the form of arrays of Object values. The “shape” of the array should match that of the Recordset. In this case, it is for an author row. Data type checking is performed on the values, and if a type coercion is not supported for a particular column, then an exception (AdoException) will be thrown by the addNew method. Applications may choose to catch this run-time exception, or allow for the core exception handler to catch the condition. This usually means aborting the current process, so you are encouraged to use proper try/catch blocks around such operations. The delete call above marks the first row of the Recordset as deleted. The row is not actually purged from the Recordset until commit is called. (For more information about commit, see the Transaction sample.) Rows that are marked as deleted enter a “row deleted” state and are not visible to the user unless the cancelUpdate method is invoked. New rows may be appended to the Recordset using the addNew method, which creates a row with null values for all fields. The update method is used to actually “push” the local changes to the data source.

Next, two Recordset properties are set to specify how data is marshaled. Data marshaling refers to the packaging, transfer, and unpackaging of data across process and/or machine boundaries. ADO/WFC supports distributed object invocation/operation and data marshaling over protocols such as HTTP 1.0/1.1 and DCOM. The following relevant properties are set for the Recordset:

rs.setMarshalOptions(AdoEnums.adMarshalModifiedOnly);
rs.setPersistFormat(AdoEnums.adPersistADTG);

The MarshalOptions property specifies that when it is time to marshal the Recordset back to a business object or to the data source, only the modified and new data will be sent. If you have a large result and only end up modifying a small minority of rows, this makes perfect sense, assuming that your business rules (objects) do not require the full result set for processing. The PersistFormat property tells the ADO run time to package the rows of the Recordset in a specific, optimized format for transfer — the ADTG tablegram streaming format defined by Microsoft. These properties are activated only when the Recordset is “pushed” from the client to the middle tier or data source.

Finally, the requery method is introduced in this sample. This method has the effect of purging the local Recordset cache and replacing it with the most recent results of the query or SQL DML used to create the Recordset. This is useful if information has become stale on the client. A simple scenario is to refresh a monitor display that lists the current prices of traded securities. ADO/WFC makes it easy to “restart with latest data.” Unlike other data access models, getting the latest data is as straightforward as requerying your Recordset.

The last step in this sample, after returning from the useRecordset method, is to close and release the resources of the Recordset. This is an important habit to get into, especially in your Java business objects when you use ADO/WFC for data access programming.


Command Sample

This sample extends the framework code by introducing an important feature of ADO/WFC: the parametrically varying Command. Command objects provide a performance gain for applications by caching execution plans for repetitive operations, which only differ in the value of the parameters plugged in at a given point. The prime example of this is a stored procedure that takes an input parameter ([in]) and returns a result set (Recordset) to the caller. The execution time saved by the program through reuse of the cached execution plan is substantial.

Note The following code assumes that you have access to the Microsoft® SQL Server™ sample database, pubs. If you do not, you can substitute your table of choice.

public boolean run()
{
   Connection c = new Connection();
   c.setIsolationLevel (AdoEnums.adXactReadCommitted);
   c.setCursorLocation (AdoEnums.adUseClientBatch);

   c.open ("dsn=aDSN;database=pubs", "aName", "aPWD");

   Command cmd = new Command();
   cmd.setName ("MyFirstCommand");
   cmd.setCommandText ("select * from authors where au_lname = ?");
   cmd.setCommandTimeout (10);
   cmd.setPrepared (true);
   cmd.setCommandType (AdoEnums.adCmdText);
   cmd.setActiveConnection ("dsn=aDSN;database=pubs ");
   cmd.setActiveConnection (c);

   Parameters p = cmd.getParameters();

   Parameter param = new Parameter();
   param.setName ("MyFirstParam");
   param.setType (AdoEnums.adVarChar);
   param.setDirection (AdoEnums.adParamInput);
   param.setSize (5);
   param.setString ("Smith");
   p.append (param);

   Recordset rs = cmd.execute();
   rs.moveFirst();

   if (rs.getFields().getItem("au_lname").getString().compareTo("Smith") != 0)
      System.out.println("The author is not a Smith.");

   rs.close();
   c.close();

   return true;
}

This sample instantiates and configures a Command object. Like all ADO/WFC components, Command objects are configured through their properties:

cmd.setName ("MyFavoriteSQLQuery");
cmd.setCommandText ("select * from authors where au_lname = ?");
cmd.setCommandTimeout (10);
cmd.setPrepared (true);
cmd.setCommandType (AdoEnums.adCmdText);

The Name property defines a friendly name for the Command. The CommandText property specifies the invariant portion of the Command; this is the portion that will be “prepared,” meaning generated and cached, by the sample code. Placeholders for parameters in the command are specified by the “?” character. The Prepared boolean property is used to compile the execution plan for the command. The CommandTimeout property is identical to the ConnectionTimeout property — it denotes an upper bound on duration for the command to successfully complete execution. The final property, CommandType, is used to specify the type of Command. This property can be used to differentiate parametric SQL statements from other types of commands, such as stored procedures.

Now, the profile of the input parameter must be delineated. Each Command has a collection of Parameter objects. You may add, remove, and edit these Parameter objects. This is precisely what the following lines of code do — specify the parameter’s name, data type, “direction” of the parameter (in, out, in/out), data size, and the value to plug into the Parameter.

Parameters p = cmd.getParameters();
Parameter param = new Parameter();
param.setName ("MyFirstParam");
param.setType (AdoEnums.adVarChar);
param.setDirection (AdoEnums.adParamInput);
param.setSize (5);
param.setString ("Smith");
p.append (param);

Once the Parameter has been defined, and a value loaded into it, the Command is ready to be invoked. In this example, the return value of the Command will be a set of rows matching the predicate of the select statement — a Recordset to be marshaled to the client. The Command is executed by invoking the execute method. The resulting Recordset is a full-fledged cursor, capable of navigation and data modifications. Data in a Recordset may be accessed through several methods, but the preferred methods are enumerated as follows:

rs.getFields().getItem("<column_name>").get<JavaIntrinsicOrStringorObjectAccessor>();
rs.getFields("<column_name>").get<JavaIntrinsicOrStringorObjectAccessor>();
rs.getItem("<column_name>").get<JavaIntrinsicOrStringorObjectAccessor>();

The <column_name> parameter should be replaced with a valid field name, such as “au_lname” in this sample. The ADO/WFC Field component provides a Java-friendly set of accessor methods for pulling the value out of the Field into a Java intrinsic type, or a Java Object. There is no need to utilize Variant types to access data within Java programs. Some data coercion operations are not supported, and will result in AdoExceptions being thrown by the Field object.

The remaining methods in this sample demonstrate the pattern of closing objects (such as Connections and Recordsets) to ensure proper release of the underlying resources.


Asynchronous Command Execution Sample

This sample demonstrates a convenient feature of ADO/WFC: the ability to asynchronously execute commands and populate large Recordsets on background threads. This sample extends the framework code by spawning a thread and using an event listener to catch the “population complete” event.

// This import statement is required for the
// delegate event callback class definition.
import com.ms.lang.*;

... // framework code

boolean done = false;
boolean execOK = false;

// Implement an event handling routine to
// catch the Recordset “population completion”
// event from the population thread.
public synchronized void onExecuteComplete(ConnectionEvent e)
{
   done = true;
   notify();
}

Recordset rs;
Command cmd;

public synchronized boolean run()
{
   execOK = false;

   Connection c = new Connection();
   c.setCursorLocation (AdoEnums.adUseClientBatch);
   c.open ("dsn=aDSN", "aName", "aPWD");

   // Establish a callback handler for the execute complete
   // event and add it to the Connection object.
   ConnectionEventHandler handler =
            new ConnectionEventHandler(this, "onExecuteComplete");
   c.addExecuteCompleteHandler(handler);

   cmd = new Command();
   cmd.setActiveConnection (c);
   cmd.setCommandText ("select * from authors");

   Runnable asyncExecute = new Runnable() {
      public void run() {
         try {
          rs = cmd.execute(null,AdoEnums.adRunAsync);
         } catch (Exception e)
            {
               System.out.println(e);
               synchronized (DemoAsyncCommand.this) {
               done = true;
               execOK = false;
               DemoAsyncCommand.this.notify();
            }
         }
      }
   };

   new Thread(asyncExecute,"RunAsync").start();
   execOK = true;

   while (!done)
      try {
         wait();
      } catch (Exception e)
      {
         execOK = false;
      }

   if (execOK)
   {
      rs.moveFirst();
      rs.moveLast();

      rs.close();
      c.close();
      return true;
   } else
      return false;
}

The first set of initialization code establishes the event handling “listener” for the Connection “execute complete” event — an event fired when the Command associated with the Connection finishes executing. A method, onExecuteComplete(ConnectionEvent e), is defined within the sample class, which implicitly triggers the notify method. The following code is a standard delegate-based event registration call sequence:

ConnectionEventHandler handler = new ConnectionEventHandler(this,"onExecuteComplete");
c.addExecuteCompleteHandler(handler);

This event model diverges sharply from the JavaBean EventSource-EventAdpater-EventListener model that ships with the JDK 1.1. The Delegate class is a member of the com.ms.lang package, and should be imported into applications that rely on event handling between components on Win32 platforms. ADO/WFC components do not support the JDK 1.02 or JavaBean event models. The Command code is basically identical to the code in the Command sample, without the parametric aspect.

Note that the run method is prefixed with the Java access modifier, synchronized. This denotes that only one active thread may be running in the method body at any given time, ensuring that race conditions do not occur. The lines that define a new Runnable object are worth examining, since this is where the execution thread is being defined.

Note The Runnable interface is implemented by classes that want to operate as threaded objects, but do not want to subclass java.lang.Thread. The Runnable interface has only one method: the entry point run, which is invoked by the thread scheduler to start the threaded object.

Runnable asyncExecute = new Runnable() {
      public void run() {
         try {
          rs = cmd.execute(null,AdoEnums.adRunAsync);
         } catch (Exception e)
            {
               System.out.println(e);
               synchronized (DemoAsyncCommand.this) {
               done = true;
               execOK = false;
               DemoAsyncCommand.this.notify();
            }
         }
      }
   };

This code instantiates an anonymous Runnable object with one defined method, run. The body of the run method performs the cmd.execute(null, AdoEnums.adRunAsync). This segment of code only prepares the thread object to run; the object must be “started” through the following call sequence:

new Thread(asyncExecute,"RunAsync").start();

This starts the Command execution, which will perform in asynchronous mode. The Thread constructor used in the sample takes, as parameters, a Runnable instance and a “thread name.” (In this sample, “RunAsync” is the name associated with the thread.) The new thread will begin executing the method body of the asyncExecute object. The first code line is a Command object executing the current command string (“select * from author”) in asynchronous mode, specified by the constant parameter passed to the method. Had no constant been passed to the execute method, the command would have executed in synchronous mode. The ADO and OLE DB components that run underneath the covers of ADO/WFC are free-threaded objects and need to be configured just like their Java counterparts.

While the command-execute thread runs in the background, the foreground thread enters a wait loop until notified, through the callback on the OnExecuteComplete event handler, that the command has finished executing. Once the event is fired from the background thread, the boolean variable done is set to true and the main thread can proceed to the task of navigating through the Recordset.

This sample is for slightly more advanced users. It is not necessary to run ADO/WFC with explicit multi-threading, but there are certain scenarios, especially those involving long wait periods, where efficient use of threads will bolster the end user experience and lead to better control flow. As always, the final lines of the sample release the Recordset and Connection resources.

Try experimenting with multi-threaded versions of the other samples, such as the Connection and Recordset samples. Or, create a version of this sample with multiple Commands being executed simultaneously; for example, “select * from authors” and “select * from publishers, titles where publishers.pubid = titles.pubid”. Also, remember to use Runnable instead of subclassing Thread directly. You can always convert to a full Thread object through the overloaded constructor.


Transaction Sample

This sample extends the framework code to demonstrate ADO/WFC’s support for transactions. Transactional-aware applications are an integral part of modern computing. ADO fully supports both local transactions, as well as distributed, two-phase transactions coordinated by the Microsoft® Distributed Transaction Coordinator (DTC). The following sample utilizes local transaction management capabilities surfaced through the Connection object. You can get the current transaction from the Connection by using the getActiveTransaction method.

public boolean run()
{
   Connection c = new Connection();
   c.setIsolationLevel (AdoEnums.adXactReadCommitted);
   c.setCursorLocation (AdoEnums.adUseClientBatch);
   c.open ("dsn=aDSN", "aName", "aPWD");

   Transaction t = c.getActiveTransaction();

   if (t != null)
   {
      Recordset rs = c.execute ("select * from authors");
      t.beginTrans();

      int recordCount = rs.getRecordCount();
      c.executeUpdate ("delete from authors");
      rs.requery();
      if (rs.getRecordCount() != 0)
      {
         rs.close();
         // Roll back (undo) the changes.
         t.rollbackTrans(false);
         c.close();
         return false;
      }

      // Make the changes permanent in the data source
      // and then refresh the results to verify the operation.
      t.commitTrans(false);
      rs.requery();
      rs.close();
   }

   c.close();
   return true;
}

The IsolationLevel property, set through c.setIsolationLevel (AdoEnums.adXactReadCommitted), is important when dealing with transactionally-scoped data access and application logic. Isolation refers to the level of autonomy that a transaction has from data/state changes made in other transactions. In the case of ReadCommitted isolation (specified by the constant parameter passed to the Connection object), any access to data sources will only see “committed” data, meaning the changes that are durable and have been successfully made. This guards against “phantom writes” that may appear if lower, more relaxed isolation levels are used.

The ADO/WFC Transaction object is dispensed from the Connection object. Once a non-null reference is obtained, the standard transaction operations may be used: beginning, committing, or aborting changes. In this sample, a new transaction is created after the initial select statement is executed from the Connection, resulting in a non-transacted Recordset. Subsequent to the t.beginTrans() call, the Recordset is replenished, after the Connection object is used to execute a SQL DML statement. This statement deletes the entire authors table. The Recordset, now empty, is within a transactional scope. The following lines then verify that the Recordset is indeed within the “scope” of the running transaction:

if (rs.getRecordCount() != 0)
   {
      rs.close();
      // Roll back (undo) the changes.
      t.rollbackTrans(false);
      c.close();
      return false;
   }

The RecordCount of the Recordset should equal zero, since the Connection deleted all of the rows. If it fails to equal zero, the entire delete operation is aborted, rolling back the state of the data source to the state it had before the transaction was started. If, on the other hand, the Recordset is empty, then the transaction has worked appropriately and the changes can be committed, as follows:

t.commitTrans(false);
rs.requery();
rs.close();

The Recordset is requeried, and should be empty, since the deletes were made permanent. The Recordset is then closed and the sample is completed. Transactions are an excellent way to write applications that ensure strange results do not creep into your control logic. ADO/WFC’s full support for both the local and distributed varieties make transactions a flexible and useful framework for building distributed business applications.


DataSpace Sample

This sample extends the framework code to show how a middle-tier business object can be invoked through the ADO/WFC component framework. This sample leverages a pre-packaged business object that ships with Remote Data Services (RDS) and Microsoft® Internet Information Server 4.0 (IIS4): the RDSServer.DataFactory. This business object provides data source querying and updates through a business object intermediary, itself. The basic principles employed in this sample are the same as those that would be used in a custom business object. If you plan to use custom business objects written in Java, Microsoft® Visual Basic®, or Microsoft® Visual C++®, be sure to register the classes properly with the application server, in order that the launch permissions allow objects to be instantiated over HTTP using the Simple Object Access Protocol (SOAP). The simple code sequence needed to invoke and marshal data is shown below.

public boolean run()
{
   ObjectProxy obj = DataSpace.CreateObject (
                        "RDSServer.DataFactory", "http://aServerName");
   Recordset rs = (Recordset) obj.call ("Query",
                new Object[] {"dsn=aDSN;uid=aUID;pwd=aPWD;database=pubs",
                "select * from authors"});

   rs.moveFirst();
   rs.moveLast();

   rs.close();
   rs.release();

   // VM tip: force garbage collection to
   // reclaim heap more efficiently.
   System.gc();
   System.runFinalization();

   return true;
}

To invoke a business object on an application server, you must create a “proxy”; that is, a stand-in for the object that is used to marshal requests and parameters to and from the client’s application process and the remote business object. ADO/WFC layers its ObjectProxy over the RDS SOAP proxy, and can use this proxy to communicate with any Automation-based business object, whether written in Java, Visual Basic, or Visual C++. The following line of code shows how the ObjectProxy is created:

ObjectProxy obj = DataSpace.CreateObject (
                     "RDSServer.DataFactory", "http://aServerName");

This code creates a new ObjectProxy through the DataSpace class method, CreateObject(prog_id, server_url). This proxy may then be used to invoke calls on the target object (in this case the DataFactory) and unmarshal/marshal Recordsets. Once the proxy is created, the method of choice can be invoked through the overloaded call method:

Recordset rs = (Recordset) obj.call ("Query",
             new Object[] {"dsn=aDSN;uid=aUID;pwd=aPWD;database=pubs",
             "select * from authors"});

This call passes an Object array of two strings (one containing the ConnectionString, the other the Source/Command) to the proxy. This internally converts the Java types into marshalable Variant data types, packages them into a SOAP request, and dispatches the request to the server-side RDS component. The parameters are then unpacked and the remote object server method (“Query”) is invoked with the parameter values. The resulting Recordset is then packaged into an ADTG stream and marshaled back to the client ObjectProxy, which unpacks the Recordset stream, creates a Recordset instance, populates the Recordset with the unmarshaled tabular data, and then returns a reference to the Recordset to the caller. This process is effectively the equivalent of the original “select * from authors” call, but through the auspices of a business object. Obviously, you can create much more sophisticated, domain-based business objects. Try to program your own Visual Basic Automation class, register it with your application server, and then invoke it with the code provided in this sample.

Return Parameter Limitation (Beta 1)

Currently, the only object type that can be returned from a middle-tier business object is an ADO Recordset. However, any valid OLE Automation types may be used as [in] parameters. This limitation will be removed in subsequent releases of ADO/WFC. The default business object, DataFactory, returns an ADO Recordset as an [out] parameter (return value), which can be used, as is, by application developers. Note that an attempt to return a non-Recordset parameter (return value) will result in a NullPointerException being thrown by the ADO/WFC run time system.


Event Handling Sample

This sample introduces the powerful new event handling architecture based on delegates for the Microsoft virtual machine (Microsoft VM). The delegate event model is a replacement for the JavaBean model and is not compatible with it. The two styles should not be intermixed in application code. This is a large sample that delineates all of the ADO/WFC Recordset events that can be registered for callback by Java application developers.

public class DemoFramework
{
   Recordset recordSet;

   RecordsetEventHandler handler =
      new RecordsetEventHandler(this,"onWillMove");
   RecordsetEventHandler handler2 =
      new RecordsetEventHandler(this,"onWillMove2");
   RecordsetEventHandler handler3 =
      new RecordsetEventHandler(this,"onMoveComplete");
   RecordsetEventHandler handler4 =
      new RecordsetEventHandler(this,"onEndOfRecordset");
   RecordsetEventHandler handler5 =
      new RecordsetEventHandler(this,"onWillChangeField");
   RecordsetEventHandler handler6 =
      new RecordsetEventHandler(this,"onFieldChangeComplete");

   public void addEventListeners()
   {
      recordSet.addWillMoveHandler(handler);
      recordSet.addWillMoveHandler(handler2);
      recordSet.addMoveCompleteHandler(handler3);
      recordSet.addEndOfRecordsetHandler(handler4);
      recordSet.addWillChangeFieldHandler(handler5);
      recordSet.addFieldChangeCompleteHandler(handler6);
   }

   public void removeEventListeners()
   {
      recordSet.removeWillMoveHandler(handler);
      recordSet.removeWillMoveHandler(handler2);
   }

   public void onEndOfRecordset(RecordsetEvent e)
   {
      System.out.println("onEndOfRecordset:" + e);
   }

   public void onMoveComplete(RecordsetEvent e)
   {
      System.out.println("onMoveComplete:" + e);
   }

   public void onWillMove(RecordsetEvent e)
   {
      System.out.println("onWillMove:" + e);
   }

   public void onWillMove2(RecordsetEvent e)
   {
      System.out.println("onWillMove2:" + e);
   }

   public void onWillChangeField(RecordsetEvent e)
   {
      System.out.println("onWillChangeField:" + e);
   }

   public void onFieldChangeComplete(RecordsetEvent e)
   {
      System.out.println("onFieldChangeComplete:" + e);
   }

   void FieldChange()
   {
      try {
         recordSet.getFields().getItem("au_lname").setString("someWeirdValue");
      } catch (Throwable t)
      {
         t.printStackTrace();
      }
   }

   public Recordset getRecordset(String strConnection, String strSQL)
   {
      Recordset rs;

      rs = new Recordset();
      rs.setCursorLocation(AdoEnums.adUseClient);
      rs.setLockType (AdoEnums.adLockBatchOptimistic);
      rs.setCacheSize (10);
      rs.setMaxRecords (1000);

      rs.open(strSQL, strConnection);
      rs.moveLast();
      rs.moveFirst();
      recordSet = rs;

      return rs;
   }

   public void ScrollForward()
   {

      recordSet.moveFirst();

      int lRows = 0;
      while (!recordSet.getEOF())
      {
         lRows++;
         recordSet.moveNext();
      }
   }

   public boolean run()
   {
      String query = "select * from authors";
      String connection = "dsn=aDSN;database=pubs";

      boolean case1 = false, case2 = false, case0 = false;
      recordSet = getRecordset(connection,query);

      addEventListeners();
      FieldChange();
      ScrollForward();
      removeEventListeners();

      recordSet.close();
      recordSet.release();

      return true;
   }

   public static void main(String args[])
   {
      new DemoFramework().run();
   }
}

A delegate can be thought of as a Java callback pointer (or function pointer). An event listener can arrange to be called back on any method it has. Delegates in ADO/WFC are wrapped within the <type>EventHandler classes. There is one EventHandler for each of the three main types of event-generating ADO components: RecordsetEventHandler, ConnectionEventHandler, and CommandEventHandler. The following code creates six RecordsetEventHandler delegates:

   RecordsetEventHandler handler =
      new RecordsetEventHandler(this,"onWillMove");
   RecordsetEventHandler handler2 =
      new RecordsetEventHandler(this,"onWillMove2");
   RecordsetEventHandler handler3 =
      new RecordsetEventHandler(this,"onMoveComplete");
   RecordsetEventHandler handler4 =
      new RecordsetEventHandler(this,"onEndOfRecordset");
   RecordsetEventHandler handler5 =
      new RecordsetEventHandler(this,"onWillChangeField");
   RecordsetEventHandler handler6 =
      new RecordsetEventHandler(this,"onFieldChangeComplete");

Each of the second parameters to the RecordsetEventHandler constructor statements above designate one of the possible Recordset object events, or conditions, that the Recordset can enter while manipulated within an application context. This sample is set up to listen to all of the events. Typically, a Java component may only be interested in a subset of that total; for example, a business object may be designed for validating any field changes in a form, but is disinterested in currency change notifications.

Note that events are comprised of before (“will”) and after (“complete”) pairs of events. Listeners that want to be able to react to an event before it happens, with the opportunity to reverse the event, typically register for the “will” events. The following code shows how to add event listeners to the source object:

recordSet.addWillChangeFieldHandler(handler);
recordSet.addFieldChangeCompleteHandler(handler2);

Once an event listener is registered, the method specified in the RecordsetEventHandler will be invoked every time an event of the specified type occurs within the object. To show how an event is triggered, examine the FieldChange method. The DemoFramework class registers itself (using the this reference) to receive both the before (“will”) and after (“complete”) events. The FieldChange method, called during the run method, then triggers the event propagation:

void FieldChange()
   {
      try {
         recordSet.getFields().getItem("au_lname").setString("someWeirdValue");
      } catch (Throwable t)
      {
         t.printStackTrace();
      }
   }

Accessing the Field value triggers the event propagation mechanism. The WillChangeFieldHandler routine receives the event first, and outputs a simple console message acknowledging receipt. Then, the FieldChangeCompleteHandler routine receives the event, and also outputs a simple console message. Of course, the behavior of the two event handling methods could have been much more elaborate; for example, throwing a CancelChangeException to roll back the proposed change. (This is equivalent to the VetoableEventListener concept of JavaBeans, but without the immense overhead of the JavaBean model.)

Familiarizing yourself with this new approach to event notifications and callback methods is a requisite for successful ADO/WFC programming in the component assembly paradigm. Classical, non-event-driven applications are still feasible without the delegate model, but most modern form-based UI applications stand to be improved through assimilation of this new event model.


Data Navigation Sample

This sample extends the framework code to demonstrate the capabilities of metadata examination of Recordsets. Since ADO/WFC’s Field objects contain both metadata and field values, the metadata for a given Field may be accessed from any position within the Recordset. There is no requirement, as in JDBC, for separate DatabaseMetadata or ResultsetMetadata-style objects. The metadata is always available.

The following code shows the procedure for extracting basic metadata information from the Fields collection of a Recordset, after the Recordset has been opened and populated.

public boolean run()
{
   Recordset rs = new Recordset();
   rs.setCursorType (AdoEnums.adOpenStatic);
   rs.setCursorLocation (AdoEnums.adUseClientBatch);
   rs.setLockType (AdoEnums.adLockBatchOptimistic);
   rs.open("select * from authors",
     "server=aServer;driver=sql server;uid=aUID;pwd=aPWD;database=pubs");

   rs.moveFirst();

   Fields fields = rs.getFields();
   int cFields = fields.getCount();

   for( int iField = 0; iField < cFields; iField++ )
   {
      useField( fields.getItem( iField ) );
   }

   for( int iField = 0; iField < cFields; iField++ )
   {
      System.out.print( fields.getItem( iField ).getName() + "\t" );
   }
   System.out.println("");

   rs.moveFirst();
   while( !rs.getEOF() )
   {
      for( int iField = 0; iField < cFields; iField++ )
      {
         System.out.print( getString( fields.getItem( iField ) ) + "\t" );
      }
      System.out.println("");

      rs.moveNext();
   }

   rs.close();
   rs.release();
   return true;
}

String getString (Field fld)
{
   if (fld.isNull())
      return "NULL";
   else
      return fld.getObject().toString();
}

void useField (Field fld)
{
   AdoProperties props = fld.getProperties();
   int cProps = props.getCount();
   for( int iProp = 0; iProp < cProps; iProp++ )
   {
      AdoProperty p = props.getItem( iProp );
      System.out.println("[" + p.getName() + " = " + p.getString() + "]");
   }

   System.out.println ("ActualSize = " + fld.getActualSize());
   System.out.println ("Attributes = " + fld.getAttributes());
   System.out.println ("DefinedSize = " + fld.getDefinedSize());
   System.out.println ("Name = " + fld.getName());
   System.out.println ("Type = " + fld.getType());
   System.out.println ("Precision = " + fld.getPrecision());
   System.out.println ("NumericScale = " + fld.getNumericScale());

}

This sample begins by using the basic ADO/WFC constants to configure the Recordset’s properties prior to opening the cursor:

rs.setCursorType (AdoEnums.adOpenStatic);
rs.setCursorLocation (AdoEnums.adUseClientBatch);
rs.setLockType (AdoEnums.adLockBatchOptimistic);

The Recordset is now ready to execute the query. After populating the client-side, batch-mode cursor, the application extracts the Fields collection, which contains the navigation mechanism for accessing individual row data values, as well as the Field metadata. The Fields collection provides a method for determining the number of Field objects it contains. This varies with the “shape” of the Recordset. Having established the dimension or shape of the Recordset, the application proceeds to extract the AdoProperty objects associated with each Field.

The useField method is the heart of the sample. This method retrieves the values of each AdoProperty of the Field, which are then written to the System console window. The code prints the core AdoProperties for Field objects: actual size, name, data type, precision, and numeric scale. The useField method demonstrates how you can easily access the metadata of a Recordset.

Having displayed the Recordset’s metadata, displaying the data values in each row is a simple matter of iterating over the Recordset from the first row until recordset.EOF is encountered:

rs.moveFirst();
while( !rs.getEOF() )
   {
      for( int iField = 0; iField < cFields; iField++ )
      {
         System.out.print( getString( fields.getItem( iField ) ) + "\t");
      }
      System.out.println("");

      rs.moveNext();
   }

Each Field in the given row is traversed and the row is accessed with the getItem accessor method. The value is then converted to a String object and written to the System console window. When the end of the current row is reached, the Recordset position is advanced to the next row by the moveNext method. The process repeats until the end of the Recordset is reached, and the program then terminates normally.


Windows Foundation Classes for Java and ADO/WFD

Windows Foundation Classes for Java and ADO/WFC

November 1998

By Greg Hinkel

Microsoft® ActiveX® Data Objects for Windows Foundation Classes (ADO/WFC) were developed to provide the Microsoft® Visual J++® programmer with a set of classes that allow easy access to both relational and nonrelational data. You will find the classes in the package com.ms.wfc.data.

The purpose of this article is to explain and clarify the usage of ADO/WFC. It provides a number of Java and WFC examples that illustrate not only how to use ADO from Visual J++, but why you would want to use ADO.

Examples illustrating how to use ADO in a Java component or application are simplified by severely limiting error checking. For the purpose of demonstration, the example is clearer if error checking and try catch blocks are limited. However, you should not limit your error checking.

In regards to error handling, you are in a much better position to determine which errors you need to be concerned about. This paper presents only a few canonical cases you will easily understand by looking at the code.

Using ADO in Java

Before ADO/WFC, there was only one way to use ADO if you programmed in Java. Visual J++ 1.1 provided a Type Library Wizard that read the ADO type library and generated a set of Java classes. You could use these classes when you wanted to use ADO in your Visual J++ code.

With Visual J++ 6.0, the natural choice for the Java ADO programmer is ADO/WFC. It provides you with command line completion for ADO/WFC and an object browser.

ADO/WFC Features

ADO/WFC is a set of classes that extend ADO interfaces in a Java-friendly manner. You can deal with data types in an object-based manner rather than as variants. ADO methods accept native ADO data types rather than variants.

In this model, performance is improved by directly accessing the OLE DB Rowset object and presenting the native field data types to you as Java types, as opposed to obtaining a variant and converting to Java type and vice-versa.

The model also exposes the ADO event notification to you through the WFC event notification model.

ADO errors are mapped to exceptions.

To use ADO/WFC, import com.ms.wfc.data and use the classes in this package.

Simple Example

Take a look at the following example. Keep in mind this example is very simple, with no exception handling at all. This example, like most of the examples in this paper, is a console application with a single class, Class1, which has a main function. The examples are as simple as possible in order to show clearly and distinctly what works.

import com.ms.wfc.data.*;

public class Class1
{
   public static void main (String[] args)
   {
      Connection conn = new Connection();
      Recordset rs = new Recordset();
      conn.open("Provider=SQLOLEDB;Data Source=(local);" +
              "User ID =sa;Password=;Initial Catalog=Pubs;");
      rs = conn.execute("select * from authors");
      for(int i = 0; i < rs.getFields().getCount(); i++ )
      {
         System.out.print(rs.getFields().getItem(i).getName() + "\t");
      }
      System.out.print("\n");
      while( !rs.getEOF() )
      {
         for( int i = 0; i < rs.getFields().getCount(); i ++ )
         {
            System.out.print(rs.getFields().getItem(i).getString() +
                                                             "\t");
         }
         System.out.print("\n");
         rs.moveNext();
      }
      rs.close();
      conn.close();
      rs.release();
   }
}

First Recordset and Connection objects are declared and initialized. The following line opens the Connection object:

conn.open("Provider=SQLOLEDB;Data Source=(local);" +
              "User ID =sa;Password=;Initial Catalog=Pubs;");

This specifies the use of the OLE DB provider for Microsoft® SQL Server™, the local SQL Server, the user ID and password, and the Pubs database.

The Connection.execute() method is called, passing in a string that contains a simple select statement. The execute() method returns a Recordset which iterates through the Fields collection and prints out the column names.

rs = conn.execute("select * from authors");
for(int i = 0; i < rs.getFields().getCount(); i++ )
{
   System.out.print(rs.getFields().getItem(i).getName() + "\t");
}

Then System.out.print("\n"); is issued to move to the next line so that the contents of the Recordset contents can be printed. Another System.out.print("\n"); separates the rows, and then both the Recordset and the Connection are closed. In the last line, rs.release() ensures that all of the Recordset object's resources are released in a timely manner.

while( !rs.getEOF() )
{
   for( int i = 0; i < rs.getFields().getCount(); i ++ )
   {
      System.out.print(rs.getFields().getItem(i).getString() + "\t");
   }
   System.out.print("\n");
   rs.moveNext();
}
rs.close();
conn.close();
rs.release();

ADO/WFC Programming Examples

The rest of the examples assume that you are already familiar with Visual J++ development. As mentioned previously, each example is a standalone console application and uses a connection to the SQL Server pubs database and tables contained in the database. You can modify the connection string and table names to adapt to your system. The examples do not deal with exception handling in detail but show some examples of how you can use it in your coding.

Connection Example

A connection is all you need to send an Update or Insert statement to the database using the Connection.executeUpdate() method. The following connection example sets several connection properties, executes an Update statement, and then prints out the number of rows affected by the statement.

It also exposes the ADO/WFC executeUpdate method. In Microsoft® Visual Basic® for Applications (VBA), the Execute method has an optional parameter called RecordsAffected. This parameter is exposed in ADO/WFC as the return value for the executeUpdate method. When using the Insert, Update, Delete, or Create methods, you should invoke the executeUpdate method. It returns the numbers of records affected by the particular Insert, Update, or Delete statement.

The following example sets the CursorLocation, ConnectionTimeout, and CommandTimeout properties. If you do not set these properties, the defaults for the particular property are used. In this example, the CursorLocation is set to use a client-side cursor that supports scrolling and caching of data; this reduces network round trips to the server. The default is a server-side, forward-only cursor.

The properties ConnectionTimeout and CommandTimeout ensure that the application has to only wait for a certain period of time for the connection and command requests to be processed.

The executeUpdate method updates author Anne Ringer's last name to "Richards" and returns the count of the records that have been updated.

import com.ms.wfc.data.*;

public class Class1
{
   public static void main (String[] args)
   {
      Connection conn = new Connection();
      String strSQL = new String();
      int iRecordsAffected;

      try
      {
         conn.setConnectionString("Data Source=(local);" +
                        "User ID =sa;Password=;");
         conn.setProvider("SQLOLEDB");
         conn.setCommandTimeout(20);
         conn.setConnectionTimeout(20);
         conn.open();
         conn.setDefaultDatabase("Pubs");
         strSQL = "update authors set au_lname = 'Richards' " +
                "where au_fname = 'Anne' and au_lname= 'Ringer'";
         iRecordsAffected = conn.executeUpdate(strSQL);
         System.out.println(iRecordsAffected + " records were updated.");
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try { conn.close(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
}

Connection Properties Example

The following example accesses the Properties collection on the Command object. In ADO, Properties collections on the Recordset, Field, Parameter, and Command objects allow each provider to expose an extensible set of properties for each object.

import com.ms.wfc.data.*;

public class Class1
{
   public static void main (String[] args)
   {
      Connection conn = new Connection();
      AdoProperties props;
      AdoProperty prop;
      int iNumProps;

      try
      {
         conn.open("Provider=SQLOLEDB;Data Source=(local);" +
                 "User ID =sa;Password=;Initial Catalog=Pubs;");
         iNumProps = conn.getProperties().getCount();
         props = conn.getProperties();
         for( int i = 0; i < iNumProps; i++ )
         {
            prop = props.getItem(i);
            System.out.println(prop.getName() + " = " +
                                 prop.getString());
         }
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try { conn.close(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
}

Connection.openSchema() Example

The Connection.openSchema() method retrieves information about a data source, such as which tables are on the server, what data types the server supports, or what columns are defined on a particular table. The openSchema method returns a Recordset that is different for each schema. The AdoEnums.Schema.TABLES schema returns a Recordset with six columns: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, TABLE_GUID, and DESCRIPTION. As another example the AdoEnums.Schema.COLUMNS schema returns a Recordset with 28 fields that completely describe each column in a table.

The following two examples show how to use the openSchema method.

Table Demo

The first example retrieves information about all the tables in the Pubs database.

import com.ms.wfc.data.*;

public class Class1
{
   public static void main (String[] args)
   {
      Connection conn = new Connection();
      Recordset rs = new Recordset();
      Field fldTableName;
      Field fldTabelType;

      try
      {
         conn.open("Provider=SQLOLEDB;Data Source=(local);" +
            "User ID =sa;Password=;Initial Catalog=Pubs;");
         rs = conn.openSchema(AdoEnums.Schema.TABLES,
            new Object[] {"Pubs",null,null,"TABLE"});
         fldTableName = rs.getFields().getItem("TABLE_NAME");
         fldTabelType = rs.getFields().getItem("TABLE_TYPE");
         while ( !rs.getEOF())
         {
            System.out.print(fldTableName.getString());
            System.out.println(" is of type " +
                        fldTabelType.getString());
            rs.moveNext();
         }
         rs.close(); rs.release();
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try { conn.close(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
}

Column Demo

The second example retrieves information about all the columns in a table. The table to be used is hardcoded.

import com.ms.wfc.data.*;

public class Class1
{
   public static void main (String[] args)
   {
      Connection conn = new Connection();
      Recordset rs = new Recordset();

      try
      {
         conn.open("Provider=SQLOLEDB;Data Source=(local);" +
            "User ID =sa;Password=;Initial Catalog=Pubs;");
         rs = conn.openSchema(AdoEnums.Schema.COLUMNS,
            new Object[] {null,null,"authors",null});
         int iCount = rs.getFields().getCount();
         if( !rs.getEOF() )
         {
            for(int i = 0; i < iCount; i++ )
            {
               Field field = rs.getFields().getItem(i);
               System.out.print(field.getName() + " = ");
               System.out.print(field.getString() + "\n");
            }
         }
         rs.close(); rs.release();
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try { conn.close(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
}

Look at the Table Demo example. It uses the name of a database (catalog) from which to print out the names of the tables contained therein.

rs = conn.openSchema(AdoEnums.Schema.TABLES, new Object[]      "Pubs",null,null,"TABLE"});

This version of the openSchema() method takes two parameters, an integer and an object array. The integer indicates the particular schema of interest, and the second parameter indicates what information to retrieve about that schema. In the AdoEnums.Schema.TABLES case, an object array with four objects is created.

In the table demo example, new Object[] "Pubs",null,null,"TABLE"} specifies which database to use, and "TABLE" indicates to return tables only. In the Pubs database that ships with SQL Server, this results in the following output:

authors is of type TABLE
discounts is of type TABLE
employee is of type TABLE
jobs is of type TABLE
Orders is of type TABLE
pub_info is of type TABLE
publishers is of type TABLE
roysched is of type TABLE
sales is of type TABLE
stores is of type TABLE
titleauthor is of type TABLE
titles is of type TABLE

Because the openSchema() method returns a Recordset, you can test for the EOF property and walk through each row printing out the "TABLE_NAME" column of the Recordset.

The following code prints this information:

while ( !rs.getEOF())
{
   Field field = rs.getFields().getItem("TABLE_NAME");
   System.out.print(field.getString());
   field = rs.getFields().getItem("TABLE_TYPE");
   System.out.println(" is of type " + field.getString());
   rs.moveNext();
}

If you change the array and omit "TABLE", for example, new Object[] {"Pubs",null,null,null} you get the following output:

sysallocations is of type SYSTEM TABLE
syscolumns is of type SYSTEM TABLE
syscomments is of type SYSTEM TABLE
sysdepends is of type SYSTEM TABLE
sysfilegroups is of type SYSTEM TABLE
sysfiles is of type SYSTEM TABLE
sysfiles1 is of type SYSTEM TABLE
sysforeignkeys is of type SYSTEM TABLE
sysindexes is of type SYSTEM TABLE
sysindexkeys is of type SYSTEM TABLE
sysmembers is of type SYSTEM TABLE
sysobjects is of type SYSTEM TABLE
syspermissions is of type SYSTEM TABLE
sysprotects is of type SYSTEM TABLE
sysreferences is of type SYSTEM TABLE
systypes is of type SYSTEM TABLE
sysusers is of type SYSTEM TABLE
authors is of type TABLE
discounts is of type TABLE
employee is of type TABLE
jobs is of type TABLE
Orders is of type TABLE
pub_info is of type TABLE
publishers is of type TABLE
roysched is of type TABLE
sales is of type TABLE
stores is of type TABLE
titleauthor is of type TABLE
titles is of type TABLE
sysalternates is of type VIEW
sysconstraints is of type VIEW
syssegments is of type VIEW
titleview is of type VIEW
CHECK_CONSTRAINTS is of type VIEW
COLUMN_DOMAIN_USAGE is of type VIEW
COLUMN_PRIVILEGES is of type VIEW
COLUMNS is of type VIEW
CONSTRAINT_COLUMN_USAGE is of type VIEW
CONSTRAINT_TABLE_USAGE is of type VIEW
DOMAIN_CONSTRAINTS is of type VIEW
DOMAINS is of type VIEW
KEY_COLUMN_USAGE is of type VIEW
REFERENTIAL_CONSTRAINTS is of type VIEW
SCHEMATA is of type VIEW
TABLE_CONSTRAINTS is of type VIEW
TABLE_PRIVILEGES is of type VIEW
TABLES is of type VIEW
VIEW_COLUMN_USAGE is of type VIEW
VIEW_TABLE_USAGE is of type VIEW
VIEWS is of type VIEW

Allowable values for the fourth parameter are "ALIAS", "TABLE", "SYNONYM", "SYSTEM TABLE", "VIEW", "GLOBAL TEMPORARY", "LOCAL TEMPORARY" or null. Null indicates not to restrict the Recordset, whereas "VIEW" indicates that you want to see the views.

The column demo example specifies a table name and prints out all 28 fields of the resulting Recordset. Only one row from the Recordset is printed, and it contains all the information you could possibly want about that column of the table.

rs = conn.openSchema(AdoEnums.Schema.COLUMNS,
         new Object[] {null,null,"authors",null});
   int iCount = rs.getFields().getCount();
   if( !rs.getEOF() )
   {
      for(int i = 0; i < iCount; i++ )
      {
         Field field = rs.getFields().getItem(i);
         System.out.print(field.getName() + " = ");
         System.out.print(field.getString() + "\n");
      }
   }

The output for one column of the authors table of the pubs database is the following:

TABLE_CATALOG = pubs
TABLE_SCHEMA = dbo
TABLE_NAME = authors
COLUMN_NAME = au_id
COLUMN_GUID = null
COLUMN_PROPID = null
ORDINAL_POSITION = 1
COLUMN_HASDEFAULT = False
COLUMN_DEFAULT = null
COLUMN_FLAGS = 4
IS_NULLABLE = False
DATA_TYPE = 129
TYPE_GUID = null
CHARACTER_MAXIMUM_LENGTH = 11
CHARACTER_OCTET_LENGTH = 11
NUMERIC_PRECISION = null
NUMERIC_SCALE = null
DATETIME_PRECISION = null
CHARACTER_SET_CATALOG = master
CHARACTER_SET_SCHEMA = dbo
CHARACTER_SET_NAME = iso_1
COLLATION_CATALOG = master
COLLATION_SCHEMA = dbo
COLLATION_NAME = nocase_iso
DOMAIN_CATALOG = pubs
DOMAIN_SCHEMA = dbo
DOMAIN_NAME = id
DESCRIPTION = null

To learn more about the different Schema rowsets you can refer to the "OLE DB Programmer's Reference" and the ADO online Help. Also, keep in mind that the various schema require differing amounts of information in the openSchema() call. For example, the catalog schema can be retrieved with the following call:

rs = conn.openSchema(AdoEnums.Schema.CATALOGS);

This version of openSchema takes a single integer parameter and the resulting Recordset will give you a list of all the catalogs on the server to which you are connected. If you know the name of the catalog you are interested in you can make the following call:

rs = conn.openSchema(AdoEnums.Schema.CATALOGS,   new Object[] {"Pubs"});

This call returns information about only the "Pubs" catalog and the Recordset returned has two columns, CATALOG_NAME and DESCRIPTION.

Long Recordset Example

The following long example shows some of the things you can do with a Recordset. Appendix A contains the complete example, but the discussion that follows concentrates only on the code that demonstrates particular functionality. You will best understand the example by examining it in sections.

The application simply calls a series of functions that do interesting things. Here is the body of the main function:

public static void main (String[] args)
{
   Class1 App = new Class1();
   App.openConnection(App.conn);
   App.printAuthors(App.conn);
   App.sortAuthors(App.conn);
   App.filterAuthors(App.conn);
   App.setMaxRecords(App.conn);
   App.printRSProperties(App.conn);
   App.useBookmarks(App.conn);
   App.multipleRecordsets(App.conn);
   App.saveRSToFile(App.conn, "c:\\RecordsetDemo\\AuthorsRS.adtg");
   App.openAndPrintRSFromFile("c:\\RecordsetDemo\\AuthorsRS.adtg");
   App.editRSFromFile("c:\\RecordsetDemo\\AuthorsRS.adtg");
   App.openAndPrintRSFromFile("c:\\RecordsetDemo\\AuthorsRS.adtg");
}

Class1 declares a Connection variable, conn, that is used throughout the example. The variable conn is intialized in the first method, openConnection. Nothing in the openConnection method is unusual.

The second method, printAuthors, prints out the rows resulting from "select * from authors".

rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
rs.setSource("select * from authors");
rs.setActiveConnection(conn);
rs.open();
printRecordset(rs);

The method printRecordset is used throughout the example to print out the Recordset. It takes a Recordset as a parameter and then prints out the field names followed by all the rows in the Recordset. Following is the relevant code for printRecordset(Recordset rs).

void printRecordset(Recordset rs)
{
   Fields fields;
   Field fld;
   int iFieldCount;

   try
   {
      fields = rs.getFields();
      iFieldCount = fields.getCount();
      //For this to be accurate we must use
      //AdoEnums.CursorLocation.CLIENT
      System.out.println("There are " + rs.getRecordCount() +
                        " rows in the recordset.");
      for( int i = 0; i < iFieldCount; i++ )
      {
         fld = fields.getItem(i);
         System.out.print(fld.getName() + "\t");
      }
      System.out.println();
      while( !rs.getEOF() )
      {
         for( int j = 0; j < iFieldCount; j++ )
         {
            fld = fields.getItem(j);
            System.out.print(fld.getString() + "\t");
         }
         rs.moveNext();
         System.out.println();
      }
      System.out.println();
   }
   //exception handling goes here

First, the Fields collection is retrieved from the Recordset and the number of fields are counted.

fields = rs.getFields();
iFieldCount = fields.getCount();

Then the number of records in the Recordset are printed.

System.out.println("There are " + rs.getRecordCount() + " rows in the recordset.");

In order for the recordcount to be accurate, the CursorLocation of all Recordset objects is set to AdoEnums.CursorLocation.CLIENT.

A reference exists to the Fields collection; it is used to walk through each field in the Recordset and print its name.

for( int i = 0; i < iFieldCount; i++ )
{
      fld = fields.getItem(i);
      System.out.print(fld.getName() + "\t");
}

Next, the value of each field in each row is printed.

while( !rs.getEOF() )
{
      for( int j = 0; j < iFieldCount; j++ )
      {
         fld = fields.getItem(j);
         System.out.print(fld.getString() + "\t");
      }
      rs.moveNext();
      System.out.println();
}

The third method, sortAuthors(), uses the Sort property of the Recordset to sort the Recordset according to one of the fields. In this case, the "state" column is used to sort the Recordset. The following code shows how to set it.

rs.setSort("state");

Next, printRecordset is called and the sorted recordset is passed in. The rows are printed in sorted order based on the "state" column.

The fourth method, filterAuthors(), uses the Filter property to restrict the rows that are visible. First, the Filter is set as follows:

rs.setFilter("state = 'CA'");

Now the Recordset can be printed out to verify that only rows with the "state" column set to 'CA' are printed.

printRecordset(rs);

The Filter property is set to an empty string so that all rows are visible again. The Recordset is printed as verification.

rs.setFilter("");
printRecordset(rs);

The fifth method, setMaxRecords(), uses the MaxRecords property to limit the number of rows in the Recordset. This is useful if you want to limit the resources used by a Recordset or the number of rows sent over the network.

MaxRecords is set to 5, the select string is specified, the Recordset is opened, and the rows are printed.

rs.setMaxRecords(5);
   rs.setSource("select * from authors");
   rs.setActiveConnection(conn);
   rs.open();
   printRecordset(rs);

The sixth method, printRSProperties(), prints the properties of an open Recordset. This is very similar to the example that printed out the properties of the Connection object.

iNumProps = rs.getProperties().getCount();
   props = rs.getProperties();
   for( int i = 0; i < iNumProps; i++ )
   {
      prop = props.getItem(i);
      System.out.println(prop.getName() + " = " + prop.getString());
   }

Following are all 105 of the properties. Keep in mind that different providers may well have a different number of properties.

IAccessor = -1
IChapteredRowset = -1
IColumnsInfo = -1
IColumnsRowset = -1
IConnectionPointContainer = -1
IConvertType = -1
ILockBytes = 0
IRowset = -1
IDBAsynchStatus = 0
IParentRowset = -1
IRowsetChange = 0
IRowsetExactScroll = -1
IRowsetFind = -1
IRowsetIdentity = -1
IRowsetInfo = -1
IRowsetLocate = -1
IRowsetRefresh = -1
IRowsetResynch = 0
IRowsetScroll = -1
IRowsetUpdate = 0
IRowsetView = -1
IRowsetIndex = 0
ISequentialStream = -1
IStorage = 0
IStream = 0
ISupportErrorInfo = -1
Preserve on Abort = -1
Access Order = 2
Append-Only Rowset = 0
Blocking Storage Objects = 0
Use Bookmarks = -1
Skip Deleted Bookmarks = -1
Bookmark Type = 1
Cache Deferred Columns = 0
Fetch Backwards = -1
Hold Rows = -1
Scroll Backwards = -1
Change Inserted Rows = -1
Column Privileges = 0
Command Time Out = 0
Preserve on Commit = -1
Defer Column = 0
Delay Storage Object Updates = -1
Private1 =
Filter Operations = 27
Find Operations = 27
Hidden Columns = 0
Immobile Rows = -1
Literal Bookmarks = -1
Literal Row Identity = -1
Maximum Open Rows = 0
Maximum Pending Rows = 0
Maximum Rows = 0
Column Writable = 0
Memory Usage = 0
Notification Granularity = 1
Notification Phases = 31
Column Set Notification = 3
Row Delete Notification = 3
Row First Change Notification = 3
Row Insert Notification = 3
Row Resynchronization Notification = 3
Rowset Release Notification = 3
Rowset Fetch Position Change Notification = 3
Row Undo Change Notification = 3
Row Undo Delete Notification = 3
Row Undo Insert Notification = 3
Row Update Notification = 3
Bookmarks Ordered = -1
Others' Inserts Visible = 0
Others' Changes Visible = 0
Own Inserts Visible = -1
Own Changes Visible = -1
Quick Restart = -1
Reentrant Events = -1
Remove Deleted Rows = -1
Report Multiple Changes = 0
Return Pending Inserts = -1
Row Privileges = 0
Asynchronous Rowset Processing = 16
Row Threading Model = 1
Server Cursor = 0
Strong Row Identity = -1
Objects Transacted = 0
Unique Rows = 0
Updatability = 7
Batch Size = 15
Update Criteria = 2
Background Fetch Size = 15
Initial Fetch Size = 50
Background thread Priority = 3
Cache Child Rows = -1
Maintain Change Status = 0
Auto Recalc = 1
Unique Table =
Unique Schema =
Unique Catalog =
Resync Command =
Cursor Engine Version = 21
Reshape Name =
Update Resync = 1
Bookmarkable = -1

The seventh method, useBookmarks(), uses an array of bookmarks to print the Recordset in reverse order. After opening the Recordset, the number of records returned is used to size an array of bookmarks.

rs.open();
iNumRecords = rs.getRecordCount();
Object bm[] = new Object[iNumRecords];

The code moves through the Recordset from first row to last printing out the "au_lname" field. The bookmark for each row is retrieved and stored in an array.

for( int i = 0; i < iNumRecords; i++ )
{
   System.out.println(rs.getField("au_lname").getString());
   bm[i] = rs.getBookmark();
   rs.moveNext();
}

Now the code moves through the bookmark array in reverse order, setting the Bookmark property and printing the "au_lname" field again.

for( int j = iNumRecords - 1; j >= 0; j-- )
{
   rs.setBookmark(bm[j]);
   System.out.println(rs.getField("au_lname").getString());
}

The eighth method demonstrates how ADO can manage a Recordset that contains multiple Recordset objects. The Source property is set to a string that contains three select statements.

rs.setSource("select * from authors;select * from titles;" +
               "select * from stores;");
rs.setActiveConnection(conn);
rs.open();

Once the Recordset is open, the "au_lname" column from each row in the first recordset is printed.

while( !rs.getEOF() )
{
   System.out.println("\t" + rs.getField("au_lname").getString());
   rs.moveNext();
}

The following line of code moves to the next Recordset.

rs = rs.nextRecordset();

The following code prints out the "title" column from each row in the second Recordset.

while( !rs.getEOF() )
{
   System.out.println("\t" + rs.getField("title").getString());
   rs.moveNext();
}

Another call to NextRecordset is executed, and the "stor_name" field from each row of the third Recordset is printed.

rs = rs.nextRecordset();
while( !rs.getEOF() )
{
   System.out.println("\t" + rs.getField("stor_name").getString());
   rs.moveNext();
}

The next three methods demonstrate how ADO can save a Recordset as a file. This functionality will allow you to take your laptop with you to another city and still make and save changes to a Recordset.

The first method opens a Recordset, executes a select statement, and saves the Recordset to a file on the local computer. You will find the code in the saveRSToFile() method. The name of the file we want to use as a parameter is passed in, the Recordset is opened and saved to the local computer.

rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
rs.setSource("select * from authors");
rs.setActiveConnection(conn);
rs.open();
rs.save(strFileName);

Notice that the Recordset is not closed here, but in the 'finally' block, which also closes the file. Once you have a file open you have exclusive access to it until you close the Recordset. So you can save the file, continue to make more changes and then save the file again. It is not until you close the Recordset that the file is actually closed.

This example uses a file with the following path: "c:\RecordsetDemo\AuthorsRS.adtg". Keep in mind that the directory "c:\RecordsetDemo\" must already exist on the computer. The Recordset.save() method will not create it. The .adtg extension refers to the format in which the Recordset is saved (Advanced Data TableGram).

Be aware that the file name you pass must not already exist on the computer. If the file already exists, the error number 80 will be returned, indicating the file already exists. This is for security reasons. You can open a new file or open an existing file but you can not overwrite an existing file that you have not opened.

The second method, openAndPrintRSFromFile(), is a helper method that prints out the "au_lname" column. It is called after the Recordset is first saved and then again after the Recordset is modified and saved. The output is as follows.

White
Green
Carson
O'Leary
Straight
Smith
Bennet
Dull
Gringlesby
Locksley
Greene
Blotchet-Halls
Yokomoto
del Castillo
DeFrance
Stringer
MacFeather
Karsen
Panteley
Hunter
McBadden
Ringer
Ringer

WhiteX
GreenX
CarsonX
O'LearyX
StraightX
SmithX
BennetX
DullX
GringlesbyX
LocksleyX
GreeneX
Blotchet-HallsX
YokomotoX
del CastilloX
DeFranceX
StringerX
MacFeatherX
KarsenX
PanteleyX
HunterX
McBaddenX
RingerX
RingerX

Now that you have a file on the local computer that is a saved Recordset, you can do something interesting with it. Open it again at a later time, maybe several days later, make changes to the Recordset and then resave the modified Recordset.

The third method, editRSFromFile() method does this. You do not need a connection for this to work, only a Recordset and a saved Recordset on your local computer. A string is passed to this method that indicates what file to use. The string opens the file. Then the code moves through the Recordset and change the "au_lname" field in each row. In fact, it adds "X" to end of each author's last name. Then the Recordset is saved back to disk and closed. The helper method is called again, printing the results to verify that the changes were saved.

rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
rs.setLockType(AdoEnums.LockType.OPTIMISTIC);
rs.open(strFileName);
while( !rs.getEOF() )
{
   rs.getField("au_lname").setString(rs.getField("au_lname").getString()
                                                               + "X");
   rs.update();
   rs.moveNext();
}
rs.save(strFileName);

The Recordset continues to be saved in a file. There are a number of interesting things you could do with it. You could send it as an attachment in email, make a backup copy, or continue to edit and update it for the next three weeks.

You could also use a business object running in Transaction Server to update the SQL Server database. To do this, create the business object and invoke a method that takes a Recordset as a parameter. The business object is then responsible for applying business rules and applying the update to the underlying SQL Server. You can implement this on a row by row basis, or you could use the Recordset.updateBatch() method to make all the changes at once. Keep in mind that no locks are placed on the database until the time the updates are applied and, yes, you will need to figure out what to do in the case that one of the rows has been changed by someone else. Business objects are discussed more thoroughly in a later section of the paper.

Command Object with Input Parameters Example

This is a longer example but it is quite simple. It involves an Insert statement with nine input parameters. You fill in the parameter information, update the command, fill in the parameters with new values and update the command again. Each time, a new row is inserted in the database.

It is important that you fill out all the parameter information completely and correctly. The example below relies on the correct setting of each parameter's data type, size, direction and value. If you are calling a stored procedure and do not want to fill out this information, then you can use the Parameters.refresh() method to automatically fill in this information. However, performance will suffer because ADO must go to the server and interrogate it for information about the stored procedure you want to use, what parameters it has and so on. In most cases if you are calling a stored procedure you know what parameters it has and your application will be faster if you take the time to explicitly tell ADO about the parameters.

This command is also prepared by calling cmd.setPrepared(true); with SQL Server this means that an execution plan will be prepared and each time the command is executed the same execution plan will be used. This speeds performance because the execution plan may well be cached by SQL Server, especially if it is used in succession so that cache hits keep the plan in memory.

import com.ms.wfc.data.*;

public class Class1
{
   public static void main (String[] args)
   {
      Connection conn = new Connection();
      Command cmd = new Command();
      String strSQL = new String();

      try
      {
         conn.open("Provider=SQLOLEDB;Data Source=(local);" +
                 "User ID =sa;Password=;Initial Catalog=Pubs;");
         cmd.setActiveConnection (conn);
         strSQL = "insert authors values (?,?,?,?,?,?,?,?,?)";
         cmd.setCommandText(strSQL);
         cmd.setCommandType (AdoEnums.CommandType.TEXT);
         cmd.setPrepared(true);

         Parameters params = cmd.getParameters();
         Parameter param[] = new Parameter[9];
         //set the properties for the first parameter, id
         param[0] = new Parameter();
         param[0].setType(AdoEnums.DataType.VARCHAR);
         param[0].setDirection(AdoEnums.ParameterDirection.INPUT);
         param[0].setSize(11);
         param[0].setString("977-72-1234");
         params.append(param[0]);
         //Second parameter, au_lname
         param[1] = new Parameter();
         param[1].setType(AdoEnums.DataType.VARCHAR);
         param[1].setDirection(AdoEnums.ParameterDirection.INPUT);
         param[1].setSize(40);
         param[1].setString("Jones");
         params.append(param[1]);
         //Third parameter, au_fname
         param[2] = new Parameter();
         param[2].setType(AdoEnums.DataType.VARCHAR);
         param[2].setDirection(AdoEnums.ParameterDirection.INPUT);
         param[2].setSize(20);
         param[2].setString("Tom");
         params.append(param[2]);
         //Fourth parameter, phone
         param[3] = new Parameter();
         param[3].setType(AdoEnums.DataType.CHAR);
         param[3].setDirection(AdoEnums.ParameterDirection.INPUT);
         param[3].setSize(12);
         param[3].setString("777-555-1233");
         params.append(param[3]);
         //Fifth parameter, address
         param[4] = new Parameter();
         param[4].setType(AdoEnums.DataType.VARCHAR);
         param[4].setDirection(AdoEnums.ParameterDirection.INPUT);
         param[4].setSize(40);
         param[4].setString("123 Main St.");
         params.append(param[4]);
         //Sixth parameter, city
         param[5] = new Parameter();
         param[5].setType(AdoEnums.DataType.VARCHAR);
         param[5].setDirection(AdoEnums.ParameterDirection.INPUT);
         param[5].setSize(20);
         param[5].setString("Seattle");
         params.append(param[5]);
         //Seventh parameter, state
         param[6] = new Parameter();
         param[6].setType(AdoEnums.DataType.CHAR);
         param[6].setDirection(AdoEnums.ParameterDirection.INPUT);
         param[6].setSize(2);
         param[6].setString("WA");
         params.append(param[6]);
         //Eighth parameter, zip
         param[7] = new Parameter();
         param[7].setType(AdoEnums.DataType.CHAR);
         param[7].setDirection(AdoEnums.ParameterDirection.INPUT);
         param[7].setSize(5);
         param[7].setString("98026");
         params.append(param[7]);
         //Last parameter, contract
         param[8] = new Parameter();
         param[8].setType(AdoEnums.DataType.BOOLEAN);
         param[8].setDirection(AdoEnums.ParameterDirection.INPUT);
         //param.setSize(1);
         param[8].setBoolean(true);
         params.append(param[8]);

         int iRecordsAffected;
         iRecordsAffected = cmd.executeUpdate();
         System.out.println(iRecordsAffected);

         //Let's insert another row
         param[0].setString("977-72-1236");
         param[1].setString("Jones");
         param[2].setString("Tom");
         param[3].setString("777-555-1234");
         param[4].setString("123 Main St.");
         param[5].setString("Seattle");
         param[6].setString("WA");
         param[7].setString("98026");
         param[8].setBoolean(true);
         iRecordsAffected = cmd.executeUpdate();
         System.out.println(iRecordsAffected);

         //You get the idea, we could do thousands more
         param[0].setString("977-72-1237");
         param[1].setString("Jones");
         param[2].setString("Tom");
         param[3].setString("777-555-1234");
         param[4].setString("123 Main St.");
         param[5].setString("Seattle");
         param[6].setString("WA");
         param[7].setString("98026");
         param[8].setBoolean(true);
         iRecordsAffected = cmd.executeUpdate();
         System.out.println(iRecordsAffected);
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try { conn.close(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
}

Parameterized Command Example

This example shows how to execute a parameterized query using a Command object. When the Command is executed it returns a Recordset. A parameterized query allows you to retrieve a particular set of records and reduces execution time in cases where the query execution plan gets cached. Thus if you have a query that you execute repeatedly, your performance would benefit if the query execution plan were cached in memory on the server. Examples of this are a Select, Insert, or Update statement, or a stored procedure that takes input parameters. Stored procedures are interesting in that they contain parameters whose Direction property can also be INPUT, INPUTOUTPUT, or RETURN type.

import com.ms.wfc.data.*;

public class Class1
{
   public static void main (String[] args)
   {
      Connection conn = new Connection();
      Command cmd = new Command();
      Recordset rs = new Recordset();
      Field fld;
      try
      {
         conn.open("Provider=SQLOLEDB;Data Source=(local);" +
                 "User ID =sa;Password=;Initial Catalog=Pubs;");
         cmd.setActiveConnection (conn);
         cmd.setCommandText( "select * from authors where au_lname = ?");
         cmd.setCommandType (AdoEnums.CommandType.TEXT);
         cmd.setCommandTimeout(10);
         cmd.setPrepared(true);

         Parameters params = cmd.getParameters();
         Parameter param = new Parameter();
         //set the properties for the parameter
         param.setType(AdoEnums.DataType.VARCHAR);
         param.setDirection(AdoEnums.ParameterDirection.INPUT);
         param.setSize(40);
         param.setString("Ringer");
         params.append(param);
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setCursorType(AdoEnums.CursorType.FORWARDONLY);
         rs.open(cmd);
         fld = rs.getField("au_lname");
         while  (!rs.getEOF())
         {
            System.out.println(fld.getString());
            rs.moveNext();
         }
         rs.close();
         rs.release();
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try { conn.close(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
}

The usual connection opening code is followed by code that sets the command properties.

cmd.setActiveConnection (conn);
cmd.setCommandText( "select * from authors where au_lname = ?");
cmd.setCommandType (AdoEnums.CommandType.TEXT);
cmd.setCommandTimeout(10);
cmd.setPrepared(true);

Setting the CommandTimeout property to 10 seconds prevents you from waiting too long if anything is wrong with the server. The CommandType property is set to AdoEnums.CommandType.TEXT. If you were calling a stored procedure you would use AdoEnums.CommandType.PROCEDURE. The Prepared property is set equal to true. This is not necessary if you are only going to execute the statement once but if you plan to use it multiple times, preparing the command will cause SQL Server to generate an execution plan for the query. It reuses this execution query when subsequent invocations of this command are run.

After setting the properties of the Command object, any parameters, denoted by the question mark symbol (?) in the query, must be defined. Command objects have a Parameters collection consisting of Parameter objects. The Parameters collection is obtained and the single parameter to the query is defined. The Direction property indicates that it is an input parameter. The Type property indicates the parameter's data type. If the data type is not compatible with the column type in the database, an error on execution of the Command will be returned. The au_lname column is a varchar(40) field in the authors table. The single parameter, param.setString("Ringer"), indicates that all rows where the au_lname column is equal to "Ringer" will be returned.

Next, two properties on the Recordset to be generated are set, the command is executed, and any resulting rows are printed.

rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
rs.setCursorType(AdoEnums.CursorType.FORWARDONLY);
rs.open(cmd);
rld = rs.getField("au_lname");
while  (!rs.getEOF())
{
   System.out.println(fld.getString());
   rs.moveNext();
}

Finally, the Recordset is closed and released, then the connection is closed.

Creating and Using Business Objects

The following examples demonstrate two of ADO's unique capabilities: enabling disconnected Recordset objects and allowing creatable Recordset objects.

In addition to saving a Recordset to a file, you can also retrieve a disconnected recordset from a COM object on another computer and save it locally. Users of ADO applications don't need to be in their regular offices or have a connection to their data server all the time. In fact, a user may not have a regular office. The fact that ADO allows you to work with both disconnected recordsets and create recordsets is very powerful.

The first example is a business object that generates a Recordset, disconnects it and then sends it to the client. The client could be on another computer, in another process on the same computer or in the same process. The client is free to modify existing rows, add rows, or delete rows. The client is also free to save the Recordset to a file. This allows the client to retrieve the Recordset hours, days, or months later, and still work with it. The business object also has a method that accepts an ADO Recordset as a parameter, reconnects to the database, and applies the updates using the Connection.execute() method. This requires generating an update string that is sent to the SQL Server. In the interest of keeping the example simple, it uses an update string rather than a stored procedure.

The second example uses ADO's ability to create a Recordset with the fields and field types that you want. You can then add rows to this Recordset and call a business object that takes a Recordset as an in parameter. This business object then applies some business rules to each row in turn and then inserts the information into the database. The business object could also send an MSMQ message for each row to the shipping and accounting departments, all as part of a transaction.

The first example business object has two methods. One returns a disconnected recordset while the other takes a disconnected Recordset as a parameter and applies the updates to the database.

import com.ms.wfc.data.*;
import com.ms.com.ComFailException;

public class CreateAndUpdateRS
{
   Connection conn;
   Recordset rs;
   public com.ms.wfc.data.IDataSource GetRS()
   {
      try
      {
         conn = new Connection();
         rs = new Recordset();
         conn.setConnectionString("Provider=SQLOLEDB;" +
                        "Data Source=(local);" +
               "User ID =sa;Password=;Initial Catalog=Pubs;");
         conn.open();
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);
         rs.setCursorType(AdoEnums.CursorType.STATIC);
         rs.setActiveConnection(conn);
         rs.setSource("select * from Orders");
         rs.open();
         Connection nullConnection = null;
         rs.setActiveConnection(nullConnection);
      }
      catch (AdoException e)
      {
         int err = (0xFFFF) & e.getHResult();
         String strMSG = e.getMessage();
         ComFailException ComExcept = new ComFailException(err, strMSG);
         throw ComExcept;
      }
      finally
      {
         try{ conn.close(); }
         catch (Exception e) {/* ignore errors */ }
      }
      return (com.ms.wfc.data.IDataSource) rs.getDataSource();
   }
   public  String UpdateRS(IDataSource rsIn)
   {
      try
      {
         conn = new Connection();
         rs = new Recordset();

         rs.setDataSource(rsIn);
         conn.setConnectionString("Provider=SQLOLEDB;" +
               "Data Source=(local);" +
               "User ID =sa;Password=;Initial Catalog=Pubs;");
         conn.open();
         rs.moveFirst();
         while ( ! rs.getEOF() )
         {
         if(rs.getFields().getItem("au_lname").getString().indexOf("'") == -1)
            {
               conn.execute("update authors set au_lname = '" +
                  rs.getFields().getItem("au_lname").getString() +
            "' where au_id = '" +
            rs.getFields().getItem("au_id").getString() + "' ");
            }
            rs.moveNext();
         }
      }
      catch (AdoException e)
      {
         int err = (0xFFFF) & e.getHResult();
         String strMSG = e.getMessage();
         ComFailException ComExcept = new ComFailException(err, strMSG);
         throw ComExcept;
      }
      return "Updates applied.";
   }
}

The client code is presented in small snippets because it is a simple client that does several things with the business object and the Recordset it sends down to the client. The main function looks like this

public static void main (String[] args)
   {
      Class1 App = new Class1();
      App.getTheRSAndSaveToFile();
      App.getRSFromFileandEdit();
      App.openAndPrintRSFromFile("C:\\JavaSample\\authorsFromBO.rs");
      App.ApplyUpdates();

   }

The first method, getTheRSAndSaveToFile(), does just that, it retrieves a recordset and saves it to a file.

void getTheRSAndSaveToFile()
   {
      Recordset rs = new Recordset();
      IDataSource dataSourceIn;

      try
      {
      CreateAndUpdateRS bizObject = new CreateAndUpdateRS();
      dataSourceIn = (IDataSource)bizObject.GetRS();
      rs.setDataSource(dataSourceIn);
      rs.save("C:\\JavaSample\\authorsFromBO.rs");
      //exception handling removed.

This is really very simple code for what it does. A COM wrapper has been added to the project, which wraps the CreateAndUpdateRS. An IDataSource variable is declared because the return from the business object's GetRS method is an IDataSource. Once the business object is created and the GetRS method is called, the IDataSource return variable is used to set the DataSource property of the local Recordset. This initializes the Recordset and it is immediately saved to a file. The Recordset is closed.

The second client method, getRSFromFileandEdit(), opens the Recordset from the saved file, edits each row, applies the updates, and saves it back to the same file. The word "File" is appended to each author's last name.

rs.open("C:\\JavaSample\\authorsFromBO.rs");
   while( !rs.getEOF() )
   {
      rs.getField("au_lname").setString(rs.getField("au_lname").getString() + "File");
      rs.update();
      rs.moveNext();
   }
   rs.save("C:\\JavaSample\\authorsFromBO.rs");

Next, openAndPrintRSFromFile() is called, which just prints the "au_lname" field to the console, verifying the changes have been made. The last method, ApplyUpdates(), creates the business object again, opens the Recordset from the file, and calls the UpdateRS method which takes the Recordset as a parameter.

Recordset rs = new Recordset();
   IUnknown dataSourceOut;

   try
   {
      rs.open("C:\\JavaSample\\authorsFromBO.rs");
      CreateAndUpdateRS bizObject = new CreateAndUpdateRS();
      dataSourceOut = rs.getDataSource();
      bizObject.UpdateRS(dataSourceOut);

The following demonstrates what the business object does with the Recordset.

public  String UpdateRS(IDataSource rsIn)
   {
try
      {
         conn = new Connection();
         rs = new Recordset();

         rs.setDataSource(rsIn);
         conn.setConnectionString("Provider=SQLOLEDB;Data Source=(local);" +
            "User ID =sa;Password=;Initial Catalog=Pubs;");
         conn.open();
         rs.moveFirst();
         while ( ! rs.getEOF() )
         {
            if( rs.getFields().getItem("au_lname").getString().indexOf("'") == -1 )
            {
               conn.execute("update authors set au_lname = '" +
                   rs.getFields().getItem("au_lname").getString() + "' where au_id = '" +
                   rs.getFields().getItem("au_id").getString() + "' ");
            }
            rs.moveNext();
         }
      }

First the DataSource or the local Recordset is set with the IDataSource that was passed in, and a connection to a database is opened. The moveFirst method is called just to ensure that every row is retrieve. Finally, !rs.getEOF() is called to walk through each row.

The following is the business logic. Using rs.getFields().getItem("au_lname").getString().indexOf("'") == -1 each row is checked to see if the author's last name has a single quote in it. If it does, the row is skipped. In the authors table, O'Leary is skipped without being updated.

The second example created an arbitrary Recordset on the client. A table was created on the server with the following structure.

create table Orders
( counter int identity,
CompanyName varchar(30),
OrderNumber int,
ItemOrdered varchar(20),
Quantity int,
Priority int,
Notes varchar(255))

The client creates a Recordset that has fields to match this table definition.

The following is the COM wrapper for the business object, BOForCreatedRecordset. The business object has one function that accepts a Recordset as a parameter. It is very similar to the UpdateRS method previously discussed.

import com.ms.wfc.data.*;
import boforcreatedrecordset.*;

public class Class1
{
   public static void main (String[] args)
   {
      try
      {
         Recordset rs = new Recordset();
         BOForCreatedRecordset bo = new BOForCreatedRecordset();
         rs.getFields().append("CompanyName", AdoEnums.DataType.VARCHAR, 30);
         rs.getFields().append("OrderNumber", AdoEnums.DataType.INTEGER);
         rs.getFields().append("Item", AdoEnums.DataType.VARCHAR, 20);
         rs.getFields().append("Quantity", AdoEnums.DataType.INTEGER);
         rs.getFields().append("Priority", AdoEnums.DataType.INTEGER);
         rs.getFields().append("Notes", AdoEnums.DataType.VARCHAR, 1000);
         rs.open();
         rs.addNew();
         rs.getFields().getItem("CompanyName").setString("A Big Company" );
         rs.getFields().getItem("OrderNumber").setInt(1);
         rs.getFields().getItem("Item").setString( "Red Widgets" );
         rs.getFields().getItem("Priority").setInt(2);
         rs.getFields().getItem("Quantity").setInt(10);
         rs.getFields().getItem("Notes").setString( "Some text goes here" );
         rs.update();
         rs.addNew();
         rs.getFields().getItem("CompanyName").setString( "Another Big Company" );
         rs.getFields().getItem("OrderNumber").setInt(2);
         rs.getFields().getItem("Item").setString( "Orange Widgets" );
         rs.getFields().getItem("Priority").setInt(1);
         rs.getFields().getItem("Quantity").setInt(20);
         rs.getFields().getItem("Notes").setString( "Some text goes here" );
         rs.update();
         Connection nullConnection = null;
         rs.setActiveConnection(nullConnection);
         System.out.println(bo.UpdateRS(rs));
      }
      catch (Exception e)
      {
         System.out.println(e.getMessage());
      }
   }
}

First, the COM wrapper class for the business object is imported.

import boforcreatedrecordset.*;

Then, the code walks through and creates each of the six fields the Recordset will have, specifying the field's name, data type, and size. The AdoEnums.DataType.INTEGER fields do not need to have a size specified.

Recordset rs = new Recordset();
   rs.getFields().append("CompanyName", AdoEnums.DataType.VARCHAR, 30);
   rs.getFields().append("OrderNumber", AdoEnums.DataType.INTEGER);
   rs.getFields().append("Item", AdoEnums.DataType.VARCHAR, 20);
   rs.getFields().append("Quantity", AdoEnums.DataType.INTEGER);
   rs.getFields().append("Priority", AdoEnums.DataType.INTEGER);
   rs.getFields().append("Notes", AdoEnums.DataType.VARCHAR, 1000);

When the Recordset object's fields are defined, rs.open() is called. This finalizes the definition of the shape, and the Recordset is created. Two rows of data are added to the Recordset by calling rs.addNew(), inserting a value into each column and then calling rs.update() for each row added.

   rs.addNew();
   rs.getFields().getItem("CompanyName").setString( "A Big Company" );
   rs.getFields().getItem("OrderNumber").setInt(1);
   rs.getFields().getItem("Item").setString( "Red Widgets" );
   rs.getFields().getItem("Priority").setInt(2);
   rs.getFields().getItem("Quantity").setInt(10);
   rs.getFields().getItem("Notes").setString( "Some text goes here" );
   rs.update();
   rs.addNew();
   rs.getFields().getItem("CompanyName").setString( "Another Big Company" );
   rs.getFields().getItem("OrderNumber").setInt(2);
   rs.getFields().getItem("Item").setString( "Orange Widgets" );
   rs.getFields().getItem("Priority").setInt(1);
   rs.getFields().getItem("Quantity").setInt(20);
   rs.getFields().getItem("Notes").setString( "Some text goes here" );
   rs.update();

A connection variable is declared so that the Recordset object's ActiveConnection property can be properly set before it is passed to the business object:

Connection nullConnection = null;
   rs.setActiveConnection(nullConnection);

Because the UpdateRS method returns a string, the string returned is printed. In this simple example it just prints out, "All went well."

System.out.println(bo.UpdateRS(rs));

Here is the code for the business object, BOForCreatedRecordset. It has a single method, UpdateRS() that takes an IDataSource parameter. The IDataSource is associated with a Recordset so that all the rows can be reviewed, an "Insert" statement can be constructed, and the Recordset sent to the database with the Connection::executeUpdate() method. Note that the ActiveConnection on the Recordset does not need to be set because Recordset.updateBatch(); is not called.

import com.ms.wfc.data.*;
import com.ms.com.*;

public class BOForCreatedRecordset
{
   Connection conn;
   Recordset rs;

   public  String UpdateRS(IDataSource rsIn)
   {
      String strSQL = new String();
      try
      {
         conn = new Connection();
         rs = new Recordset();
         rs.setDataSource(rsIn);

         conn.setConnectionString("Provider=SQLOLEDB;"
                  + "Data Source=(local);User ID=sa;"
                  + "Password=;Initial Catalog=pubs;");
         conn.open();
         rs.moveFirst();
         while( !rs.getEOF() )
         {
            strSQL = "insert Orders values( '"
               + rs.getFields().getItem(0).getString()         //CompanyName
               + "'," + rs.getFields().getItem(1).getString()   //OrderNumber
               + ",'" + rs.getFields().getItem(2).getString()   //ItemOrdered
               + "'," + rs.getFields().getItem(3).getString()   //Quantity
               + "," + rs.getFields().getItem(4).getString()   //Priority
               + ",'" + rs.getFields().getItem(5).getString()   //Notes
               + "' )";
            conn.executeUpdate(strSQL);
            rs.moveNext();
         }
      }
      catch (AdoException e)
      {
         int err = (0xFFFF) & e.getHResult();
         String strErrorMSG = e.getMessage();
         ComFailException ComExcept = new ComFailException(err, strErrorMSG);
         throw ComExcept;
      }
      finally
      {
         try      { conn.close(); }
         catch   (Exception e) {/* ignore errors */ }
         return "All went well.";
      }
   }
}

IDataSource is associated with a local Recordset:

   rs.setDataSource(rsIn);

The connection to the database is opened and the moveFirst method is called on the Recordset. The code then walks through each row using a while loop. As the Insert statement is constructed, single quotes are placed around the fields that are character based, such as CompanyName, ItemOrdered, and Notes. The insert statement looks like this: "insert Orders values('Another Big Company',2,'Orange Widgets',20,1,'Some text goes here' )"

rs.moveFirst();
   while( !rs.getEOF() )
   {
      strSQL = "insert Orders values( '"
         + rs.getFields().getItem(0).getString()
         + "'," + rs.getFields().getItem(1).getString()
         + ",'" + rs.getFields().getItem(2).getString()
         + "'," + rs.getFields().getItem(3).getString()
         + "," + rs.getFields().getItem(4).getString()
         + ",'" + rs.getFields().getItem(5).getString()
         + "')";
      conn.executeUpdate(strSQL);
      rs.moveNext();
   }

The two rows are inserted into the table and a string is returned to the console application so that you can print something for the user to see.

Appendix A

import com.ms.wfc.data.*;
public class Class1
{
   Connection conn = new Connection();
   String m_strFileName = "c:\\JavaDemo\\AuthorsRS";
   public static void main (String[] args)
   {
      Class1 App = new Class1();
      App.openConnection(App.conn);
      App.printAuthors(App.conn);
      App.sortAuthors(App.conn);
      App.filterAuthors(App.conn);
      App.setMaxRecords(App.conn);
      App.printRSProperties(App.conn);
      App.useBookmarks(App.conn);
      App.multipleRecordsets(App.conn);
      App.saveRSToFile(App.conn, "c:\\RecordsetDemo\\AuthorsRS.adtg");
      App.openAndPrintRSFromFile("c:\\RecordsetDemo\\AuthorsRS.adtg");
      App.editRSFromFile("c:\\RecordsetDemo\\AuthorsRS.adtg");
      App.openAndPrintRSFromFile("c:\\RecordsetDemo\\AuthorsRS.adtg");
   }
   void openConnection(Connection conn)
   {
      try
      {
         conn.open("Provider=SQLOLEDB;Data Source=(local);" +
                 "User ID =sa;Password=;Initial Catalog=Pubs;");
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
   }
   void editRSFromFile(String strFileName)
   {
      Recordset rs = new Recordset();

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setLockType(AdoEnums.LockType.OPTIMISTIC);
         rs.open(strFileName);
         while( !rs.getEOF() )
         {
                  rs.getField("au_lname").setString(rs.getField("au_lname").getString()
                                                    + "X");
            rs.update();
            rs.moveNext();
         }
         rs.save(strFileName);
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void saveRSToFile(Connection conn, String strFileName)
   {
      Recordset rs = new Recordset();

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setSource("select * from authors");
         rs.setActiveConnection(conn);
         rs.open();
         rs.save(strFileName);
      }
      catch(AdoException e)
      {   //if err == 80 then the file already exists
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void openAndPrintRSFromFile(String strFileName)
   {
      Recordset rs = new Recordset();

      try
      {
         rs.open(strFileName);
         while( !rs.getEOF() )
         {
            System.out.println(rs.getField("au_lname").getString());
            rs.moveNext();
         }
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void multipleRecordsets(Connection conn)
   {
      Recordset rs = new Recordset();

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setSource("select * from authors;select * from titles;" +
                        "select * from stores;");
         rs.setActiveConnection(conn);
         rs.open();
         System.out.println("First Recordset\nColumn: "
                  + rs.getFields().getItem(1).getName());
         while( !rs.getEOF() )
         {
            System.out.println("\t"
                  + rs.getField("au_lname").getString());
            rs.moveNext();
         }
         rs = rs.nextRecordset();
         System.out.println("\n\nSecond Recordset\nColumn: "
                  + rs.getFields().getItem("title").getName());

         while( !rs.getEOF() )
         {
            System.out.println("\t" + rs.getField("title").getString());
            rs.moveNext();
         }
         rs = rs.nextRecordset();
         System.out.println("\n\nThird Recordset\nColumn: "
                  + rs.getFields().getItem("stor_name").getName());

         while( !rs.getEOF() )
         {
            System.out.println("\t" +
                     rs.getField("stor_name").getString());
            rs.moveNext();
         }
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void useBookmarks(Connection conn)
   {
      Recordset rs = new Recordset();
      int iNumRecords;

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setSource("select * from authors");
         rs.setActiveConnection(conn);
         rs.open();
         iNumRecords = rs.getRecordCount();
         Object bm[] = new Object[iNumRecords];
         System.out.println("Original order:");
         for( int i = 0; i < iNumRecords; i++ )
         {
            System.out.println(rs.getField("au_lname").getString());
            bm[i] = rs.getBookmark();
            rs.moveNext();
         }
         System.out.println("Bookmark array order:");
         for( int j = iNumRecords - 1; j >= 0; j-- )
         {
            rs.setBookmark(bm[j]);
            System.out.println(rs.getField("au_lname").getString());
         }
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void printRSProperties(Connection conn)
   {
      Recordset rs = new Recordset();
      AdoProperties props;
      AdoProperty prop;
      int iNumProps;

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setSource("select * from authors");
         rs.setActiveConnection(conn);
         rs.open();
         iNumProps = rs.getProperties().getCount();
         props = rs.getProperties();
         for( int i = 0; i < iNumProps; i++ )
         {
            prop = props.getItem(i);
            System.out.println(prop.getName() + " = "
                              + prop.getString());
         }
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }

   }
   void filterAuthors(Connection conn)
   {
      Recordset rs = new Recordset();

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setSource("select * from authors");
         rs.setActiveConnection(conn);
         rs.open();
         rs.setFilter("state = 'CA'");
         PrintRecordset(rs);
         //This wipes out the filter so that we have all rows again
         rs.setFilter("");
         PrintRecordset(rs);
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void setMaxRecords(Connection conn)
   {
      Recordset rs = new Recordset();

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setMaxRecords(5);
         rs.setSource("select * from authors");
         rs.setActiveConnection(conn);
         rs.open();
         PrintRecordset(rs);
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void sortAuthors(Connection conn)
   {
      Recordset rs = new Recordset();

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setSource("select * from authors");
         rs.setActiveConnection(conn);
         rs.open();
         rs.setSort("state");
         PrintRecordset(rs);
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void printAuthors(Connection conn)
   {
      Recordset rs = new Recordset();

      try
      {
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setSource("select * from authors");
         rs.setActiveConnection(conn);
         rs.open();
         PrintRecordset(rs);
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
      finally
      {
         try {rs.close(); rs.release(); }
         catch (Exception e) { /* Ignore close errors */ }
      }
   }
   void PrintRecordset(Recordset rs)
   {
      Fields fields;
      Field fld;
      int iFieldCount;

      try
      {
         fields = rs.getFields();
         iFieldCount = fields.getCount();
         //For this to be accurate we must
         //use AdoEnums.CursorLocation.CLIENT
         System.out.println("There are " + rs.getRecordCount()
                     + " rows in the recordset.");
         for( int i = 0; i < iFieldCount; i++ )
         {
            fld = fields.getItem(i);
            System.out.print(fld.getName() + "\t");
         }
         System.out.println();
         while( !rs.getEOF() )
         {
            for( int j = 0; j < iFieldCount; j++ )
            {
               fld = fields.getItem(j);
               System.out.print(fld.getString() + "\t");
            }
            rs.moveNext();
            System.out.println();
         }
         System.out.println();
      }
      catch(AdoException e)
      {
         int err  = (0xFFFF) &  e.getHResult();
         System.out.println(e.getMessage() + "  hr = " + err);
      }
      catch(Exception exception)
      {
         System.out.println(exception.getMessage());
      }
   }
}


ADO/WFC vs. JDBC

August 1998

Abstract

This article compares the Java JDBC interface to SQL data versus the Microsoft® Windows Foundation Classes (WFC) Microsoft® ActiveX® Data Objects (ADO) and also versus the OLE DB–based data solution that ships with Microsoft® Visual J++® and WFC. Specifically, the object model, programming model, and functionality of JDBC are compared with the corresponding features of ADO/WFC. The result of this comparison shows that JDBC is a limited API specifically targeted at the transfer of data from an SQL database into the Java client's buffers. JDBC does not provide a good model for interacting with the data. It does not provide access to non-SQL data and does not provide an architecture for building data components.

JDBC Object Model and ADO Equivalents

From the consumer point of view, JDBC defines four "objects."[1] The following table shows how the four JDBC objects relate to equivalent objects in ADO.

Note The JDBC documentation breaks the objects down into interfaces, types, and classes. However, for the sake of comparison with OLE DB and ADO, these are equivalent to objects.

JDBC object | ADO equivalent
DriverManager | Connection
Connection | Connection
Statement | Command
PreparedStatement | Prepared Command
CallableStatement | Command
ResultSet | Recordset

The functionality of these JDBC objects can be described as follows:

  • The DriverManager object loads drivers and provides support for creating new database connections. (It is also similar to hEnv in ODBC and to the Data Source object in OLE DB.)

  • The Connection object defines a connection to a particular database. (It is also similar to a connected hDBC in ODBC and to a Session object in OLE DB.)

  • The Statement object sets and executes an SQL statement. (This object is equivalent to hStmt in an unexecuted state in ODBC and to the Command object in OLE DB.) The Statement object has two subtypes:
    • The PreparedStatement subtype prepares the statement for repeated execution. (PreparedStatement is similar to hStmt in the prepared state in ODBC and to a Command object that supports ICommandPrepare in OLE DB.)

    • The CallableStatement subtype allows the statement to call stored procedures. (CallableStatement has no equivalent in ODBC, OLE DB, or ADO. In those APIs, stored procedures are represented and executed like any other statements.)
  • The ResultSet object represents the results of an executed row-returning statement. (It is also equivalent to hStmt in ODBC in an executed state and to the Rowset object in OLE DB.) This is a separate object from the Statement object.

Programming Model: JDBC vs. ADO

The JDBC and ADO programming models are compared in the table below and discussed in the list following the table.

Feature | JDBC | ADO
Programming level | Low | High
Types of data | SQL only | Any
Language | Language-neutral | Language-neutral
Typing | Strongly typed | Both dynamic typing and static typing
Data ownership | Client-owned | Shared

  • Programming level. JDBC is intended to be a low-level API that supports basic SQL functionality. The authors of JDBC expect higher-level APIs to be defined and possibly implemented on top of JDBC.[2]

  • Types of data. JDBC is intended to be an API to SQL data only. To pass compliance tests and be called "JDBC COMPLIANT," the driver must support at least ANSI SQL-92 Entry Level.[2] SQL is used to define a result set, and executing an SQL positioned UPDATE, INSERT, or DELETE statement is the only mechanism for updating data in a result set.

  • Language. JDBC's strongest selling point might be that it has been the only native API for accessing SQL databases from within Java. However, the release of ADO for WFC definitely changes this.

  • Typing. JDBC is very strongly typed. There are separate setxxx and getxxx methods for setting each type of parameter or for retrieving each type of data from a result set. For example, an integer is retrieved from a column in a row of a result set by using getInt(); a long value is retrieved by using getLong(). There are two variants of each getxxx method: One retrieves according to index, and the other retrieves by column name. In the end, there are 34 different methods just for getting a column of data.

    This strong typing also applies to property setting and retrieval as well as to metadata retrieval. A separate method gets and sets each property, and separate methods retrieve each type of metadata.

    In comparison, ADO and OLE DB support both late bindings (using variants) and early bindings to a variety of data types. (All SQL and Automation data types are supported.) ADO has been extended specifically to work well with Java. Variants used in ADO methods and properties have been converted into native data types, and the syntax for all properties has been converted to be more like Java—for example, getProperty, setProperty, isProperty.

  • Data ownership. The JDBC model for data ownership is, like the ODBC model, client-owned. That is, the client reads data from the database directly into its internal buffers. The JDBC 2.0 specification allows scrollable result sets, absolute and relative positioning, and alternative update mechanisms. However, it is not clear how many shipping JDBC drivers support these features.

    In contrast to JDBC, ADO supports a number of different cursor type and lock types. Some of these features depend on the underlying OLE DB provider's implementation. But because ADO ships with its own cursor engine, which is implemented as an OLE DB service component, it typically enhances the cursoring functionality of the underlying provider. Data can be fetched asynchronously and can be loaded into the client cursor engine for disconnected operations. If the consumer asks for extended functionality that is not available in the underlying provider, ADO can invoke the cursor engine component transparently to provide the requested service. On disconnected recordsets, ADO also supports advanced features—such as sort, filter, and find—through the cursor engine component.

Functionality: JDBC vs. ADO

The functionality of JDBC is compared to ADO in the following table.

Feature | JDBC | ADO
Data manipulation language (DML) |  |
Opening rowsets | SQL only | SQL SELECT statement; passing in the table name or object name; from a stored procedure; from a persisted file referred to by an URL
Creating tables | SQL-based | DDL extensions
Database admin | N/A | DDL extensions
Result set functionality |  |
Scrollability | Scrollable | Scrollable
Update/insert/delete | SQL only; JDBC 2.0 allows method-based updates; supports stored procedures through callable statements | From stored procedure or SQL; using provider-specific command; also method-based—for example, using the addNew, Update, and Delete methods on the Recordset object
Row retrieval | Single row | Multiple rows
Column retrieval | Single column | Multiple
Database resync[1] | No | Yes
Notifications | No | Yes
Index navigation | No | Yes. ADO Recordset has an Index property, and a Seek method to move along the index.
Null handling | Extra method | VARIANT_NULL
Result set shape | Flat tabular only | Hierarchical recordsets are supported
Transactions |  |
Isolation levels | All | All
Auto-commit support | Yes | Yes
Nested transactions | No | Yes
Distributed transactions | Yes | Yes
Extensibility |  |
Properties | Method-based | Extensible
Object navigation | No | Yes
Error model | Throw | Error object

[1] This refers to the ability to re-retrieve the current values for a fetched row from the database. It is necessary for things like optimistic concurrency in the batched-update case.

The following list summarizes the results of the above comparison relating to JDBC functionality:

  • DML. In JDBC, as in ODBC, result sets are generated from SQL statements or stored procedures (through callable statements). In JDBC, it is not possible to "open" an object as you can in ADO (or in OLE DB). Similarly, in JDBC, tables are created and modified only by executing SQL DDL statements.

    There is no common way to do database administration in JDBC or ODBC because, for example, there is no defined SQL grammar to create a database.

  • Result set functionality. JDBC 2.0 now allows scrollable result sets, absolute and relative positioning, and alternative update mechanisms. However, it is not clear how many shipping JDBC drivers support this.

    The standard procedure for updating in JDBC is based on SQL positioned UPDATE and DELETE statements that modify the data underlying the current row. Updating in ADO is accomplished simply by calling specific methods on the Recordset object.

    JDBC 2.0 also now allows direct updates based on dynamic and keyset cursors and defines static, keyset, and dynamic cursor modes. Additionally, it allows scrollable result sets that support forward and backward scrolling.

    In JDBC, data retrieval from the result set is by a single column at a time. This limitation was identified as a performance issue for ADO, which is why ADO now can retrieve a row at a time as an array of variants, or multiple rows.

    JDBC does not have the concept of refetching the current values for a previously fetched row. Without locking the record when read, the user cannot determine whether the underlying record has changed. ADO, on the other hand, implements a sophisticated resynchronize mechanism. Users can customize the resync operation by specifying a stored procedure that should be called to resync the recordsets. Users can choose to resynchronize just the underlying values or all values. ADO also supports automatic resync on update operations. Users can specify what to resync, such as identity columns, conflicting rows, timestamp columns.

    Because JDBC is inherently a single-user model, it has no need for notifications. Because it is based on SQL, JDBC does not expose the concept of indexes. In contrast, ADO supports and implements a rich event-handling model.

    Because JDBC is strongly typed, attempting to read a null value from a column will return a zero value for that column. To distinguish between the zero value and the null value, the user must call the wasNull() method immediately after retrieving the zero value to see whether it was a null value or was actually the zero value.

    ADO returns null values with a special data type of VARIANT_NULL. ADO also implements an isNull() method that can be called even before retrieving the column value.

  • Transactions. JDBC reflects the simple transactional capabilities of SQL. It supports all of the common isolation levels and also supports an "auto-commit" mode. JDBC 2.0 supports distributed transactions.

    ADO supports distributed as well as nested transactions. ADO also implements specific methods to retrieve properties of the underlying data store. Common properties are available at the connection, command, recordset, and field level. In addition to specific methods to access properties common to most providers, ADO provides a Properties collection that exposes provider specific properties.

  • Extensibility. Error handling in JDBC is similar to ADO/WFC in the sense that errors are thrown as exceptions. However, ADO/WFC also exposes the ADO Errors collection that can be used to view extended information and multiple errors.

JDBC Interface Functionality Summary

JDBC is a low-level interface to SQL data, callable from Java. It is not a generic data access solution, it is not a high-level API, and it is not an architecture for building components. Because of its limited scrollability and query-based update semantics, it is suitable for little more than read-only, sequential retrieval of SQL data from a Java application.

Extended Functionality of OLE DB and ADO

OLE DB takes traditional data access to the next higher level. It defines and makes universal data access a reality. Because it is based on COM and is not just an API, OLE DB functionality is typically implemented via independent components. The concept of service components makes it easier to implement simple OLE DB providers and still provide a guaranteed level of functionality to the consumers. Some already-shipping service components and other useful OLE DB providers are the cursor engine component, the MSPersist provider, the MSDataShape provider for hierarchical cursors, the MSRemote provider, and Heterogeneous Query Processors from ISG Software and shipping with Microsoft® SQL Server™ 7.0.

OLE DB Extensibility

To serve component scenarios, OLE DB was designed from the ground up to be extensible so that components could build on a common set of interfaces without being limited in their ability to expose extended functionality.

Componentization

The vast majority of information today does not reside in a relational database but exists as what we call "personal data"—that is, data in mail files, spread sheets, log files, directory systems, and so on. The ability to harness this information is critical to the success of any data architecture.

JDBC is designed as a call-level API to SQL data. As with ODBC, to expose your data through JDBC you must make it look like SQL data. This assumption is present in the way the programmer obtains a result set, updates values, obtains schema information, and in how the data is modeled. Therefore, exposing JDBC requires building a query processor and relational engine on top of each of those mail files, spreadsheets, log files, and so forth. The alternative is to put each of those types of data into a relational database and use content indexing to attempt to query those types of data. Neither solution allows the universal access to personal data necessary to have all types of “information "at your fingertips."

OLE DB addresses this need because it is designed as a component architecture. Through a component architecture, data stores can easily expose only their native functionality, and general, reusable components can be written to implement common extensions to that native functionality. Because the core interfaces consumed by the component are the same as those exposed (that is, the rowset interfaces), the components can aggregate this functionality very efficiently. Because the functionality on each object is factored into interfaces, components can augment functionality without duplicating it. The result is a flattened component architecture, where each component is operating directly on the data, rather than an architecture that layers each component and requires calls and data to pass through and be handled at each layer.

As a drawback to its functionality, JDBC's extremely strong typing makes it difficult to perform bulk operations. Columns are set and retrieved one at a time. Properties are set or retrieved one at a time. While this may be acceptable for an end-user interface, it does not provide the type of low-level efficiency necessary to build good, optimized data components.

Another area of concern in JDBC is the lack of well-defined error cases, whereas a great deal of time and effort has gone into defining error cases for both ODBC and OLE DB. This type of information may not be important to a high-level interface but is integral to the building and debugging of low-level data providers and components.

Finally, JDBC does not provide any type of support for using, creating, or even knowing about any type of index information. Building efficient query processors over large quantities of non-SQL data requires leveraging indexes where they exist or can be created.

Non-SQL Data Retrieval

ADO/WFC is based on the underlying ADO/OLE DB infrastructure and hence allows access to a wide range of data. OLE DB providers can implement their own command objects. Executing the IOpenRowset::OpenRowset method on the connection object can also open recordsets, in case the provider doesn't implement the command object. This allows a recordset to be opened by just pointing to the object containing the data. Some non-SQL OLE DB providers that are already available or under development are OLE DB Simple Provider from Microsoft Corporation, Lotus Notes Provider from ISG Software, and OLE DB Provider for Microsoft Exchange.

By contrast, JDBC's mechanism for generating result sets, as in ODBC, is to execute SQL SELECT statements or stored procedures. This does not lend itself well for allowing non-SQL providers to expose result set interfaces over raw tabular data or other non-SQL based data stores.

Providing Scrollability

Even though JDBC 2.0 allows scrollable cursors, very few drivers actually implement these. Similarly, very few ODBC drivers implement this optional functionality. But Microsoft implements scrolling in a "Cursor Library" in ODBC 2.0, which provides scrollability against any forward-only driver. Similarly, ADO ships with the cursor engine component, which is an OLE DB service component, that can implement static, client-side scrollable and updatable cursors against any OLE DB provider.

Bookmarks

Bookmarks provide the mechanism for returning to a previously fetched row and are necessary for implementing a shared model where multiple clients are accessing the same result set. If supported, bookmarks are a separable but very useful extension to scrolling.

The cursor engine component shipping with ADO can implement bookmarks against arbitrary OLE DB providers. JDBC, on the other hand, must depend upon the underlying driver to implement support for bookmarks.

Deferred Updating

A critical component for efficient updating in a high-latency environment such as the Internet is the ability to batch updates. The easiest way to do this is by implementing a deferred update model, where modifications to individual rows are buffered until an explicit update call is defined.

ADO can operate in two modes of batch updating: single-row mode and batch mode. JDBC 2.0 allows batch updates, but the support for resynchronization is not clearly defined. For a good, customizable implementation of optimistic concurrency–based batch updating, it is necessary to provide methods to read the current value, the original value, and the underlying value of a column in a specific row in the result set. How this is implemented in JDBC is not clear; JDBC defines one Getxxx method for each type, and the JDBC implementation needs to be expanded to include means for reading the original and underlying values.

ADO implements recordset filtering, which makes it simple to create sub-recordsets that may contain only rows that the user is interested in. ADO also provides predefined filter enums that allow filtering for records with pending data, records that failed updates because of conflicts, and so forth.

ADO also ships with MSRemote and MSPersist OLE DB providers. These providers implement sophisticated functionality that allows users to do data-manipulation in distributed and mobile scenarios. MSPersist provider enables ADO recordsets to be persisted in the local file system and reopened. MS Remote provider allows ADO to work with OLE DB providers that are on remote machines, connected over standard network protocols like HTTP and DCOM. Thus, the users don't need to install OLE DB providers on client machines.

Hierarchical Cursors

ADO implements hierarchical recordsets that are provided through chapters in OLE DB. The MSDataShape provider makes it easier to define hierarchies on top of flat result sets returned by the underlying OLE DB providers.

JDBC does not provide any type of support for hierarchical cursors. SQL3 has added concepts to the language for generation of hierarchical result sets but does not provide a mechanism to bind to the results.

Abstract Data Types (ADTs)

As has already been discussed, one of JDBC's big limitations is its nonextensible type system. This is even more apparent when it comes to ADTs.

Java supports a concept of an "object," and JDBC allows obtaining a stream over a column for reading or writing an object. This method basically treats all objects as BLOBs and does not allow the type of functionality nor flexibility that object-relational databases are looking for.

With ADO\WFC, however, any COM object can exist as a column of the recordset. It is easy to obtain the actual object as a value of the field and to work with it independently in Visual J++ 6.0.

Summary

JDBC continues to be a very limited interface that is targeted primarily at transferring data from an SQL database to a Java client. It is immature in terms of both the functionality it provides and the details necessary for implementing database-oriented applications based on JDBC.

ADO/WFC, on the other hand, offers a much more mature platform for building complex data-oriented applications using Visual J++ 6.0. With the component architecture of OLE DB, ADO/WFC can be used to dynamically access data from a variety of data sources, including SQL databases and non-SQL sources such as e-mail stores. ADO/WFC also offers a much richer programming model for navigation and working with the data.

Footnotes

[1] Section 2.1, "A SQL level API", JavaSoft JDBC: A Java SQL API, Graham Hamilton & Rick Cattell, Version 1.01, August 8, 1996.

[2] Section 2.2, "SQL Conformance", JavaSoft JDBC: A Java SQL API, Graham Hamilton & Rick Cattell, Version 1.01, August 8, 1996.


OLE DB/ADO: Making Universal Data Access a Reality

May 1998

Abstract

Microsoft® Universal Data Access is a platform for developing multitier enterprise applications that require access to diverse relational or nonrelational data sources across intranets or the Internet. Universal Data Access consists of a collection of software components that interact with each other using a common set of system-level interfaces defined by OLE DB. Universal Data Access components consist of data providers, which contain and expose data; data consumers, which use data; and service components, which provide common services that extend the native functionality of the data store.

After reviewing the Universal Data Access platform, this paper describes two key aspects of the platform. First, it presents an overview of Microsoft® ActiveX® Data Objects (ADO), a provider-neutral, application-level data access object model that can be used from languages such as Microsoft® Visual Basic®, Java, C++, Microsoft® Visual Basic® Scripting Edition (VBScript), and Microsoft® JScript®. Second, it describes a service component architecture that enables you to compose services transparently to consumers and providers. Four services in the Universal Data Access platform work with each other via the service component architecture. A cursor service provides an efficient, client-side cache with local scrolling, filtering, and sorting capabilities. A synchronization service provides batch updating and synchronizing of data cached in the client or middle tier. A shape service allows the construction of hierarchically organized data with dynamic behavior under updates. Finally, a remote data service provides efficient marshaling of data in multitier environments over connected or disconnected networking scenarios.

Introduction

Modern data-intensive applications require the integration of information stored not only in traditional database management systems, but also in file systems, indexed-sequential files, desktop databases, spreadsheets, project management tools, electronic mail, directory services, multimedia data stores, spatial data stores, and more. Several database companies are pursuing a traditional database-centric approach, which this paper refers to as the universal database. In this approach, the database vendor extends the database engine and programming interface to support new data types, including text, spatial, video, and audio. The vendor requires customers to move all data needed by the application, which can be distributed in diverse sources across a corporation, into the vendor’s database system. This process can be expensive and wasteful.

Universal Data Access is an effective alternative to the universal database approach. However, it also can be implemented in a complementary fashion with the universal database approach. The premise of Universal Data Access is to allow applications to efficiently access data where it resides without replication, transformation, or conversion. Open interfaces allow connectivity among all data sources. Independent services provide for distributed queries, caching, update, data marshaling, distributed transactions, and content indexing among sources.

Vaskevitch outlined the initial motivation and goals for OLE DB and the Microsoft component database approach in the paper Database in Crisis and Transition: A Technical Agenda for the Year 2001. Blakeley, in Data Access for the Masses through OLE DB, presented an overview of the architecture, objects, and key interfaces for OLE DB. This paper continues the theme of these two previous papers, detailing key components that are making Universal Data Access a reality and describing how the OLE DB service component architecture works to provide run-time extensibility of database functionality to applications.

Universal Data Access Overview

OLE DB is the Microsoft system-level programming interface to diverse sources of data. OLE DB specifies a set of Microsoft® Component Object Model (COM) interfaces that encapsulate various database management system services. These interfaces enable you to create software components that comprise the Universal Data Access platform.

Whereas OLE DB is a system-level programming interface, ADO is an application-level programming interface. ADO, based on Automation, is a database-programming model that allows enterprise programmers to write applications over OLE DB data from any language including Visual Basic, Java, VBScript, JScript, and C/C++.

The following figure illustrates the Universal Data Access architecture. There are three general kinds of database components: data providers, services, and consumers.

Data providers are components that represent diverse sources of data such as SQL databases, indexed-sequential files, spreadsheets, document stores, and mail files. Providers expose data uniformly using a common abstraction called the rowset.

Services are components that extend the functionality of data providers by implementing extended interfaces where not natively supported by the data store. For example, a cursor engine is a service component that can consume data from a sequential, forward-only data source to produce scrollable data. A relational query engine is an example of a service over OLE DB data that produces rowsets satisfying a Boolean predicate.

Consumers are components that consume OLE DB data. Examples of consumers include high-level data access models such as ADO; business applications written in languages like Visual Basic, C++, or Java; and development tools themselves.

All interactions among components in the previous figure, indicated by bidirectional arrows, can occur across process and machine boundaries through network protocols such as Microsoft® Distributed COM (DCOM) or HTTP. Transacted interactions among components are possible via a distributed transaction coordination service such as the one included in Microsoft® Transaction Server (MTS).

OLE DB defines a hierarchy of four main objects as illustrated in the following figure.

The Data Source object encapsulates functions that identify a particular data provider, verify that the user has the appropriate permissions to connect to the provider, and initialize the connection to a particular data source (for example, a file name or database name).

The Session object defines the transactional scope of work done within a connection.

The Command object encapsulates the functions that enable a consumer to invoke the execution of data-definition or data-manipulation commands such as queries.

Rowset objects provide a common representation of data. They are typically created either directly from a session or as a result of a command execution, but may be generated as the result of other methods that return data or metadata.

Before each OLE DB object is created, the caller has the opportunity to request the capabilities or behavior of the data source that need to be surfaced by the object through its interfaces. This is an important aspect to remember when reading the section titled “Service Components.”

ActiveX Data Objects

ActiveX Data Objects (ADO) is a set of high-level Automation interfaces over OLE DB data. Although OLE DB is a powerful interface for manipulating data, most application developers do not need the low level of control that OLE DB gives over the data-access process. Most developers are not interested in managing memory resources, manually aggregating components, and other low-level operations. Additionally, developers often use high-level languages that do not support function pointers and other C++ call mechanisms. ADO provides the following advantages to programmers:

  • Ease of use. The data-access task analogous to “Hello World” requires only three lines of code.

  • Programming language neutral. ADO can be used from languages such as Visual Basic, Java, C++, VBScript, and JScript.

  • Provider neutral. ADO can access data from any OLE DB source. In addition, ADO adapts gracefully to support less functional OLE DB providers.

  • No loss of OLE DB functionality. ADO allows C++ programmers access to the underlying OLE DB interfaces.

  • Extensible. ADO can dynamically expose properties specific to a data provider via collections of provider properties. In addition, it provides type extensibility by allowing access to COM objects exposed as column values (for example, rowset and stream).

Object Model

ADO is made up of seven objects as described in the following figure.

The Connection, Command, and Recordset objects are top-level objects that you can create and destroy independently of each of the other objects. Though you can create the Parameter object independently of a Command object, it must be associated with a command before you can use it. The Field, Error, and Property objects each exist only within the context of their parent objects, and can’t be separately created.

The ADO Connection object encapsulates the OLE DB Data Source and Session objects. It represents a single session with the data source. The Connection object defines properties of the connection, assigns the scope of local transactions, provides a central location for retrieving errors, and provides a point for executing schema queries.

The ADO Command object encapsulates the OLE DB Command object. The Command specifies the data-definition or data-manipulation statement to be executed. In the case of a relational provider, this is an SQL statement. The Command object allows you to specify parameters and customize the behavior of the statement to be executed. A collection of Parameter objects exposes the parameters.

The ADO Recordset object encapsulates the OLE DB Rowset object. The Recordset object is the actual interface to the data, whether it is the result of a query or was generated in some other fashion. The Recordset object provides control over the locking mechanism used, the type of cursor to be used, the number of rows to access at a time, and so on. The Recordset object exposes a collection of Field objects that contain the metadata about the columns in the recordset, such as the name, type, length, and precision, as well as the actual data values themselves. Use the Recordset object to navigate through records and change data (assuming the underlying provider is updatable).

Each of the top-level ADO objects contains a collection of Property objects. The Property object allows ADO to dynamically expose the capabilities of a specific provider. Because not all data providers have the same functionality, it is important for the ADO model to allow you to access provider-specific settings in a dynamic way. This also prevents the ADO model from being cluttered with properties that are available only in certain circumstances.

Architecture

ADO is provider neutral. That is, although it allows you to access the full range of OLE DB data, it does not implement its features specific to any one provider or data source. In addition, ADO itself does not add functionality on top of the provider. Augmenting the functionality of providers, such as the ability to scroll or update, is the job of service components. One benefit is that you can take any piece of code that can manufacture an OLE DB Rowset and wrap that object in an ADO Recordset object, thereby enabling scripting of that data. Because the Recordset object isn’t needed to connect to the data source or execute the command, it needs only the core rowset interfaces (that is, IAccessor, IColumnsInfo, IConvertType, IRowset, and IRowsetInfo) to provide access to the data.

Language-Specific Extensions

Automation implements the ADO interface. Automation defines Variants as its data types. Variants work well in languages such as Visual Basic, which are not strongly typed. If you are writing in C++ or in Java, most operations in the code will require native data types to support the standard library features of the language. In these cases, with ADO you can convert data between the Variant and native data types when passing arguments.

ADO has extensions designed specifically to support both Java and C++. For C++, a set of interfaces that directly retrieve and set native C types from the underlying OLE DB provider improve performance and decrease the lines of code required to interact with the ADO interface.

For Java, ADO extensions include a set of Java classes that extend the ADO interfaces and create notifications interesting to the Java programmer. These extensions expose functions that return Java types. In addition, the extensions are optimized to improve performance. The Java class directly accesses the native data types in the OLE DB Rowset object and returns them to the Java programmer as Java types without first converting them to and from a Variant.

Sample Code

Below are two samples, one written using VBScript in the Active Server Pages (ASP) environment (which embeds script code within HTML), and one written in Java.

VBScript Sample

<html>

<body>

<% Set cn = Server.CreateObject("ADODB.Connection")
Dim rs As ADODB.Recordset

cn.Open "dsn=pubs;uid=sa;pwd="
set rs = cn.Execute("SELECT * FROM authors") %>

<table border=1>

<% while rs.EOF <> True %>

<tr>

Java Sample

import com.ms.wfc.data.*;

public class test

{
   public static void main(String args[])
   {
      Connection cn = new Connection();
      Recordset rs = new Recordset();
      cn.open("dsn=pubs", "sa", "");
      rs = cn.execute("SELECT * FROM authors");
      while( !rs.getEOF() );
   }
}

Service Components

Providing a common interface to diverse data stores containing mission-critical data is not an easy task. Each data store supports different sets of functionality, making it difficult to define a useful level of interaction between a generic application and diverse stores. Defining the level of interaction to be only the common functionality among all stores does not provide a useful set of functionality for the application. Further, this approach limits the ability of the data store to expose innovative extensions. On the other hand, requiring a rich level of functionality would force a simple data store to implement, in full or in part, a relational engine on top of its data. Such barriers prevent many types of simple data from participating in data access.

The Microsoft Universal Data Access solution is to define a rich set of functionality, but to factor that functionality so that data stores are able to expose only the subset that makes sense for their data. Thus, the data store is not limited in the functionality it can expose, but at the same time is not forced to implement extended database features in order to provide common access to its data.

This makes OLE DB attractive for the data store, but to stop there would be near to defining a least-common-denominator approach for the application. In order to be generic, the application would have to be filled with extensive conditional code to verify whether a particular set of functionality was supported. In many cases, the application would be forced to implement extensions internally to compensate for data stores that could not support extended functionality natively.

Generic, Reusable Components

To solve this problem, Universal Data Access provides a component architecture that allows individual, specialized components to implement discrete sets of database functionality, or “services,” on top of less capable stores. Thus, rather than forcing each data store to provide its own implementation of extended functionality, or forcing generic applications to implement database functionality internally, service components provide a common implementation that any application can use when accessing any data store. The fact that some functionality is implemented natively by the data store, and some through generic components, is transparent to the application. In order to understand how OLE DB components are combined to guarantee a rich functionality, it is necessary to understand COM interface factoring and aggregation.

Factored Interfaces

In COM, a single object exposes one or more interfaces. Each interface exposed by the object represents a fixed set of functionality, such that the total functionality of the object is factored among the various interfaces. Each interface inherits functionality directly or indirectly from a common interface on the object called IUnknown, such that the IUnknown interface is the “super parent” of every other interface supported by the object. IUnknown contains AddRef and Release methods, which control the lifetime of the object through reference counting, and a QueryInterface method for navigating between the different interfaces on the object. The following diagram shows a single COM object that implements two interfaces, IRowset and IRowsetScroll, in addition to IUnknown.

The full set of functionality defined in the OLE DB specification is factored into multiple discrete interfaces that group related levels of functionality for each object. This interface factoring is what makes OLE DB so accessible to simple data stores. Rather than forcing every data store to implement a full relational engine in order to be accessible through common tools, the data store implements an object comprised of only those interfaces that represent native functionality for that data store. At a minimum, this includes core interfaces that provide a simple forward-only, read-only view of the data.

COM Aggregation

A COM object can be created using another object’s IUnknown interface, in which case each interface exposed by the newly created object inherits functionality from this existing “Controlling Unknown.” The controlling object then manages the navigation between sets of functionality supported by interfaces implemented on the inner object and sets of functionality supported by interfaces on the controlling object.

The following diagram shows a COM object that supports the IRowset interface, and a controlling object that implements the IRowsetScroll interface. The IUnknown interface exposed by the controlling object navigates between the interfaces supported by the two different objects. To the application, this appears as a single object that implements both IRowset and IRowsetScroll.

Service Components

In OLE DB, a service is implemented as a controlling object that aggregates the native object exposed by the data store. When the application requests an interface that represents some set of functionality natively supported by the data store, the service returns the data store’s native interface and the application interacts with the data store directly. When the application requests some set of functionality not supported by a native interface on the data store, the service can provide a standard implementation of that functionality, interacting with the data store through the same native interfaces exposed to the application. In this manner, the application always gets the most direct, efficient path to the data. Whether the functionality is implemented natively by the data store or through an external service is completely transparent to the application.

To maximize performance and minimize overhead, the services themselves are factored into multiple components—only those services needed at any one time are loaded into memory. Thus, if a particular data store requires multiple services to satisfy the requirements of the application, multiple service components may be invoked.

Invocation of Services through Component Managers

The next problem is the actual invocation of the individual service components. Putting the logic for invoking service components within the application complicates the application code, forcing it to know the particular capabilities of the data store and the available services. Putting the logic within the data store complicates the code for the data store, limiting the ability to extend the functionality of the data store to those services known to the data store at the time its OLE DB interface was written. Furthermore, a growing number of data stores are exposing OLE DB interfaces today without any knowledge of existing services. To work with those existing data stores, the logic must live outside the data store.

The commonly held belief that any problem can be solved given enough levels of indirection proves true in this case, where the dynamic invocation of multiple services at run time is handled through thin controlling objects called component managers. Component managers aggregate each of the primary OLE DB objects exposed by the data store and ensure that the proper services are invoked to provide required functionality. Each interface supported by the data store’s native object, as well as the interfaces supported by each of the individual service components, inherit from this Component Manager’s IUnknown interface. Thus, all interface navigation goes through the Component Manager, which intercepts interfaces at any time and invokes additional services as needed. All of the logic for invoking services can be localized in this Component Manager, and new components can be added that make existing applications more functional without adding any additional code to either the application or the data store.

The following diagram shows a simple OLE DB Rowset object that exposes forward-only data retrieval through IRowset, aggregated by a Service Component that exposes scrollbar support through IRowsetScroll. Even though the Component Manager is present, calls to IRowsetScroll go directly to the Service Component while calls to IRowset go directly to the OLE DB Rowset object.

The Component Manager’s ability to intercept interfaces comes into play particularly in the creation of one object from another. The following diagram shows the Data Source Component Manager (DCM) aggregating the Data Source Object.

When the application creates a Session, the DCM intercepts the call and first creates a Session Component Manager (SCM), then creates a Session aggregated by the SCM. Similarly, when the application creates a Command from the Session, the SCM first creates a Command Component Manager (CCM), then creates a Command aggregated by the CCM. Finally, when the application creates a Rowset, the CCM first creates a Rowset Component Manager (RCM), then creates a Rowset aggregated by the RCM. At any step along the way, the individual component managers can invoke additional services to add additional functionality on top of the data store’s Data Source, Session, Command, or Rowset objects. Thus, once a Data Source Component Manager correctly aggregates the initial Data Source object, the remaining objects are automatically aggregated with their own instances of Component Managers.

At this point, the only remaining problem involves ensuring that a Data Source Component Manager correctly aggregates the initial Data Source object. OLE DB 2.0 provides a common entry point through the IDataInitialize or IDBPromptInitialize interfaces. These interfaces are exposed by a Data Initialization component distributed as part of the core OLE DB services for version 2.0. Generating a Data Source object from either of these interfaces will return the provider's Data Source aggregated by a Data Source Component Manager. Once the Component Manager aggregates the initial Data Source object, neither the application nor the data store needs any knowledge of extended services or the components that support them. The Component Managers invoke the specialized, highly optimized services necessary to provide the application a rich data model when accessing any type of data.

As explained in the previous sections, Universal Data Access enables the data flow between a data consumer and any kind of data provider. Frequently, as the data moves through each tier of the application architecture, it has to be cached, filtered, or reshaped, and needs to be synchronized with the original data in the provider. However, many providers deliver their data as forward-only, read-only rowsets. The gap between the rich functionality demanded by a typical data application and that offered by most data providers is filled by services. The following sections describe four specific services that you can combine using the service component architecture just presented.

The Cursor Service

An important challenge for a multitier, distributed application is to find a balance between keeping data current, or live, and reducing network traffic. Live data produces high network traffic and is impractical for most applications. It requires every change at the application level to be immediately propagated to the data source, and concurrent changes to the data source to be immediately pushed to the client application. In addition, every time the application scrolls beyond its local buffer, new data must be fetched to ensure dynamic membership of the data set. Another side effect of using live data is that it virtually kills application scalability. An Internet application where thousands of client requests need to be served from a data source simply does not work with live data.

A Cursor Service proves a good solution for this challenge. It provides a client cache for data sets (or cursors) and a service that implements smart synchronization mechanisms (see “The Synchronization Service.”) Using these services dramatically reduces network traffic while giving you full control over data synchronization. The Cursor Service can be installed on each tier of a multitier application, providing a great solution for scalability.

Architecture

The Cursor Service consumes data from a rowset, stores the data locally, and exposes the data along with rich data-manipulation functionality through rowset interfaces. The Cursor Service can provide a local instance of virtually any rowset produced by an application.

The Cursor Service is typically invoked when the consumer requests a local copy of a remote rowset, or when the consumer requests rowset functionality not supported by the provider's native rowset.

All rowsets are handled independently by the Cursor Service. Rowsets don’t share common data, and no integrity constraints are enforced. The Cursor Service doesn’t try to replicate the provider data model, its integrity restrictions, or its data. It rather manipulates independent data sets as defined and required by the application.

Functionality

The Cursor Service provides the following functions: data fetching, data manipulation, local updates, and local rowsets.

Data Fetching

When the consumer requests functionality that requires the Cursor Service, the Cursor Service component is invoked to fetch the data and cache it locally. The data is stored in memory buffers and, if needed, written to temporary files on disk. The data fetch can be performed synchronously or asynchronously. The fetch of large data fields, such as BLOBs, can be delayed until the data is actually required by the application.

Data Manipulation

The rowset is accessible to you via ADO/OLE DB interfaces. In ADO, use the Recordset object to maintain currency and manipulate the data (see “ActiveX Data Objects”). The Recordset exposes methods implemented by the Cursor Service to position on a row, get field values, and filter and sort the rows based on one or more fields. A Recordset can be cloned, which produces a second Recordset based on the same underlying rowset data. Clones have their own currency, filter, and sort order. The Cursor Service also generates events exposed by the associated rowset and used to bind rowset data to visual controls. Rowsets can be persisted in an internal binary format or in XML. A client rowset can be remoted between two processes or machines over DCOM or HTTP (see “Remote Data Service”), enabling distributed data access for multitier architectures over intranets and the Internet.

Local Updates

You can make any changes to the local data: modify data fields, delete existing rows, and insert new rows into the rowset. The changes are immediately committed to the cache and are visible from all the cloned Recordset objects on that rowset. Local changes can be committed to the data provider row by row or once for the entire rowset via the Synchronization Service.

Local Rowsets

The Cursor Service allows you to create a stand-alone rowset, by first defining the column information associated with the rowset and then opening it. Local rowsets can be used as data buffers or to hold and share local temporary data in an application. Indexes can be created on local rowsets to support sort, filter, and find operations. All data-manipulation operations are available on local rowsets.

The Synchronization Service

For applications using client rowsets over SQL data, the Synchronization Service component provides the ability to send changes back to the provider and to refresh the data in local rowsets with current data from the provider. Both the update and refresh operations can be applied to one or more rows simultaneously and can be coupled together in a single user command. Both operations ensure consistency of multitable rowsets if the structure of the join relation between the participating tables is known.

Architecture

The Synchronization Service has two distinct components: the Metadata Gatherer, which provides metadata information such as base table names, base column names, primary keys, and timestamp columns; and the Update/Resync component, which sends changes to the provider and returns data changes to the client. The Synchronization Service operates in conjunction with the Cursor Service, which is responsible for gathering and storing the metadata and for storing the data copies of each changed row necessary for optimistic conflict resolution. The following figure illustrates the interaction of the Synchronization components (indicated by the labeled bracket).

The synchronization process has the following stages:

First, the Cursor Service reads all the data from the underlying provider rowset and populates the local rowset. In this process, it collects and stores the basic metadata information for the rowset such as number of columns; column name, type, and precision; base table and column names; primary key columns; and timestamp/version columns.

If some metadata information is not available from the rowset provider, the Cursor Service calls the Metadata Gatherer to obtain this information. If the Metadata Gatherer still cannot obtain the metadata information, the client application can explicitly set it.

After the rowset data has been modified, the client requests to propagate the changes to the data source. The rows to be updated are passed to the Update/Resync component of the Synchronization Service with a flag stating whether the original rowset needs to be refreshed after the update. This component reads the updated metadata from the Cursor Service rowset and submits the updates to the data source. It then sets the update status on each row in the rowset participating in the update.

Depending on the flags set when the update was submitted, the Synchronization Service could update the conflicting rows in the rowset with new values inserted by concurrent users of the data store.

All the update information available for a rowset is considered an integral part of the rowset. The Cursor Service can disconnect the rowset from the underlying data source, persist it in the local file system, or remote it across process/machine boundaries. At a later stage, the updated rowset may be reconnected to the same session that originally created it (if it is still alive) or to a new session. After the reconnection, all the update information is available and the rowset can be updated via the Synchronization component.

Functionality

The Synchronization Service performs the following functions: propagates updates to the data source, refreshes the data from the data source, and coordinates conflicting updates.

Updates

The Update Service has two modes of sending the updates back to the data provider: immediate and deferred. In immediate mode, changes are committed to the provider on a row-by-row basis. In deferred mode, the changes made to the rowset are buffered locally and the row’s state is marked as changed. When the application decides to commit the changes, it calls the batch update method on the rowset and all buffered changes are sent to the data source.

Using a local static rowset with deferred updates is a common application scenario, offering the following three benefits. First, it provides fast data navigation because after the data is fetched, most operations are local. Second, it eliminates heavy concurrency in accessing data on the server. Optimistic conflict resolution allows you to resolve any conflicts. Finally, it reduces the overall network traffic associated with propagating changes to the data source by grouping multiple individual updates in a single request.

Refresh

The Refresh function updates the rowset with the most recent data available from the provider. The scope of the Refresh command could be a large data field, a row, a set of rows, or the entire rowset. To perform a refresh, the Update Service requires information about the base tables used to produce the rowset and their primary keys.

Optimistic Conflict Resolution

For each row that has changed in a rowset, the Cursor Service maintains a copy of the original row fetched from the provider. When the updates are sent to the server, the Synchronization Service compares original values with the current values on the server to verify whether the row has changed since the data was fetched. The Synchronization Service compares the values using a combination of key columns, row timestamps, changed fields, and sometimes the entire row.

If a conflict is detected, the status for that row changes to indicate an update conflict. You have the option to get the current values for the conflicting row and compare them with the original row values and the changed values in the rowset. After you resolve the conflict, the update can be submitted again, this time using the current values to detect new update conflicts. The update service has the option to get current data for all conflicting rows in a batch. It also allows you to control how the local data is updated when conflicts are detected, to match the transaction model implemented by the application.

The Shape Service

The Shape Service provides a hierarchical view of data and the reshaping capabilities that user interfaces and reporting tools require. This service works on top of the Cursor Service; it can create new hierarchical rowsets or aggregate existing rowsets into hierarchies. It implements three types of hierarchies, based on relation, parameters, and grouping. Hierarchical rowsets offer a flexible view of data. For example, an application could initially display the top level of a hierarchy, then let the user drill down into one or more levels of detail. Also, the same hierarchy can then be reshaped locally to offer a different perspective on the data.

Architecture

To implement hierarchical results, the Shape Service adds a special column in the parent rowset for each of its children in the hierarchy. For each parent row, this column contains a reference to the corresponding child rowset. This reference is called a chapter handle in OLE DB and a recordset reference in ADO. A shape command defines a hierarchical result with clauses for each type of parent-child link (relation, parameter, and grouping).

Each type has a well-defined semantic, which governs the way the component results are created and related to each other. A hierarchical result can be arbitrarily complex and can include relations of any type among its components. More specifically, relation and group hierarchies can have as building blocks flat results and any type of hierarchical results. Parameterized hierarchies can have any rowset as parent but not as child, because the child is built dynamically upon accessing the data.

The previous diagram shows the implementation of hierarchical rowsets. When a hierarchy is created, the provider adds a column to the parent rowset to provide, for each row, a reference to its child rowset. When the value of a child column is requested, the Shape Service evaluates the parent-child relation for that row, then applies the corresponding filter to the child rowset and returns the resulting chapter handle. If the call is made from ADO, a recordset based on that chapter handle is returned. Different child rowsets of a relation hierarchy may share common rows. Child rowsets of a parameterized hierarchy are fetched dynamically and are always considered to have distinct rows. All data-manipulation and synchronization methods available for flat rowsets are also available for hierarchies. Similarly, events are available for hierarchical results. The following sections briefly define the three hierarchy types.

The Relation Hierarchy

Two source rowsets, a parent and a child, create the relation hierarchy, and it usually models a one-to-many relationship between the two rowsets. The definition specifies the relation between the parent and the child via an equality predicate between two fields. When a child reference is requested, the child is filtered for the fields that match the corresponding field in the parent, and the resulting chapter or rowset is returned. Unlike hierarchical snapshots produced by a data source, the relation hierarchy is dynamic. For example, if the controlling parent field changes, the corresponding child rowset will change as well.

The Parameterized Hierarchy

The parameterized hierarchy is similar to the relation hierarchy. The parent rowset is static and fetched up front. But the child rowset is defined using a parameterized command, having parent fields as command parameters. Initially the child contains no data. When a child reference is required, the shape provider executes the command and fetches the child data that matches the current parameter fields in the parent row. The child data for each row can either be saved locally and subsequently treated as static data or refetched each time a child reference is required. Because the child is dynamic, parameterized hierarchies can only be leaves in a multilevel hierarchy.

The Group Hierarchy

This hierarchy requires only one input rowset that will become the child of the group hierarchy. The shape provider performs a grouping on this rowset and stores the result in a new local rowset, the parent of the group hierarchy. The parent has a column to hold the group details for each row. Group hierarchies are static and can be created on top of any other hierarchy.

Functionality

The Shape Service, based on the Cursor Service, provides the following functions:

  • Creates relational, parameter, or group hierarchies.

  • Creates hierarchies by reshaping client rowsets.

  • Extends the functionality provided for flat results to hierarchical data.

  • Allows creation of aggregate columns over children.

  • Enables creation of client-calculated columns within a record.

  • Enables the addition of empty user-defined columns to a result set.

The hierarchy syntax aggregates a result, as defined by a command or a name, and constructs a new hierarchy. A command is a cursor definition native to the data provider. Commands can contain parameters, which customize the hierarchy when it is created. A name refers to existing local results already built by the shape provider. The Shape Service can add columns to a new result. The new columns can be an aggregation function over child rowsets, an expression, or just a new user-defined column.

The value of the child reference column is by default different for each row because the Shape Service creates a new chapter every time. The provider can optionally return a unique child reference for a given child, and update the membership of the respective child chapter on row changes to contain the child rows for the current parent row.

The Shape Service applies the requery method called on the root rowset of a hierarchy to the entire hierarchy. All other functions such as navigation and synchronization are associated with each component rowset and should be called as needed on each component of a hierarchy.

Remote Data Service

The Microsoft® Remote Data Service (RDS) component delivers the promise of Universal Data Access to “stateless” environments such as the Internet and the World Wide Web. RDS creates a framework that permits easy and efficient interaction with OLE DB data sources on corporate intranets and over the Internet. RDS provides the advantages of client-side caching of data results, updatability, and support for data-aware ActiveX controls. It provides the rich OLE DB/ADO programming model for manipulating data to browser-hosted applications.

Motivation/Scenarios for Use

Remote Data Service transports the rich client experience, implemented by the Cursor Service component, to applications connecting over the Internet or other networks where it is difficult or expensive to maintain a consistent connection. The following are the typical features of distributed applications running over disconnected networks such as the Internet.

Server Scalability and Network Bandwidth

Scalability of Web or application servers is tested every day by users accessing applications over the Internet. Even when the servers can scale to handle large numbers of Internet users, network bandwidths often cannot. Client computers must be able to accomplish more with less support from the server, and at times without even a server connection. Server resources need to be handled judiciously by new brands of applications.

To solve the scalability problem, RDS moves the application logic (that is, the user interface and client-side data manipulation) and data to the client. It implements a “stateless” model, in which client and server can be disconnected between data access operations. RDS marshals data efficiently between the middle tier or server and the client; it provides support for batch updates via an efficient client-side Cursor Service component that can process data locally without constant server requests. The work done at the client can be saved locally and later uploaded to the server when a connection is available.

Distributed Applications and Untrusted Clients

Some of the most important tasks that Web and application server administrators handle are data integration, security, and application management. Organizations deploy business components that run on the application servers behind the Web servers and control complete access to underlying enterprise data. These components implement business rules and validation logic, allowing client applications to read and update data. The client applications make requests to the components that are under central control of the system administrator. RDS makes it easy to invoke these components over DCOM and HTTP, and efficiently marshals input/output parameters, enabling effective distributed computing.

Architecture

Remote Data Service is designed as an OLE DB service component. It builds on the foundation laid by the Cursor Service component and utilizes its services on the client machine as a data buffer. Consumers (that is, applications running on client machines) can use the RDS OLE DB provider to open OLE DB Rowset or ADO Recordset objects—these recordsets are originally created on middle tiers or servers where OLE DB data sources reside and then remoted (marshaled by value) to client machines. The following code snippets show how a remote, disconnected Recordset object is opened in ADO:

Dim rs as new ADODB.Recordset
rs.Open "Select * from Customers", "Provider=MS Remote; Remote Server=http://www.mysite.com; Remote provider = MSDASQL; DSN=pubs"

The only difference between the code shown here and that required to open a recordset when direct access to an OLE DB data source is available, is the format of the connection string. In the direct-access scenario, the code looks like this:

Dim rs as new ADODB.Recordset
rs.Open "Select * from Customers", "Provider=MSDASQL; DSN=pubs"

In the remoted case, the connection string identifies the server where the data source resides, the OLE DB provider to be used there to open the rowset, and any other information (such as a server name) needed to open the rowset.

The MS Remote provider is a standard OLE DB provider that supports standard OLE DB objects like Data Source, Session, and Command objects, and acts as a front end for providers across the network. For the Rowset object, it uses the Cursor Service component’s rowset implementation. It buffers user requests on the Data Source, Session, and Command objects and processes them one after the other, as singular requests, across the network.

As shown in the previous figure, RDS enables two powerful scenarios for client-side applications. A consumer application can either specify the command and connection strings needed to open a rowset directly through an OLE DB provider, or it can invoke a business object on the server and marshal input and output parameters efficiently over HTTP and DCOM networks. Server administrators configure the server stub of the Remote provider where they can specify commands that can be executed or business components that can be invoked on behalf of remote clients.

Compare the two scenarios. Boxes with boldface labels represent objects implemented by the remoting service component.

In the OLE DB provider scenario, when a request is made to execute a command or open a rowset (step 1), all the settings are streamed across the network to a Remote provider stub on the server (step 2). The stub opens the rowset at the server by executing consumer requests (step 3). The OLE DB provider creates a rowset as the result of the request. This rowset is then streamed out on the wire in an efficient and compact format, and reconstructed using the Cursor Service component at the client by the Remote provider (step 4). An ADO recordset is handed back to the consumer application (step 5).

In an HTTP or DCOM network scenario, when a request is made to invoke an object (steps a and b), all the settings are streamed across the network to a Remote provider stub on the server (step c). The stub invokes the required component by executing consumer requests (step d). The invoked business component creates a rowset and returns it as an out-parameter (step e). This rowset is then streamed out on the wire in an efficient and compact format, and reconstructed using the Cursor Service component at the client by the Remote provider (step f). A client-side rowset is handed back to the consumer application (step g).

No proprietary network protocols are needed to work with RDS. It works over standard protocols like HTTP and DCOM. DCOM is now available on all Win32 platforms, Macintosh®, and UNIX platforms. Here is a code snippet that shows how client applications can invoke business objects on the server:

Dim ProxyGen as new RDS.DataSpace
Dim BusProxy as object

Dim rsCust as ADODB.Recordset
Set CustProxy = ProxyGen.CreateObject("OrderEntry.Customer", "http://www.mysite.com")
Set rsCust = CustProxy.GetCustomer

RDS also allows local persistence of remoted data. The original values read by the server, along with changes made by the client, are persisted. A client can go offline and transmit the changes to the server at convenient times without losing valuable changes. Data persistence does not require any sophisticated data store or personal database on the client machine. The following code snippet shows how a recordset can be saved and reopened.

Dim rs as new ADODB.Recordset
rs.Open "Select * from Customer", "Provider=MS Remote;Remote Server="http://www.mysite.com";DSN=pubs"
…
rs("Cust_Status") = "Preferred"   // Update customer records
…
rs.movenext
rs("zip_code") = 98052
…
rs.save "c:\ADO\recordsets\customer.rs"   // Persist the recordset in a local file
rs.close
…
Dim rs1 as new ADODB.Recordset
rs1.open "c:\ADO\recordsets\customer.rs"   // Reopen the recordset
rs1.updatebatch            // Propagate the changes back to the server
…

When a client requests updates to be propagated back to the server or middle tier, the Remote provider streams the rowset, including all changes and original values, back to the application server. At the server, the Cursor Service component reconstructs it. The Cursor Service component and the Synchronization component work together to update and resynchronize the rowset, as explained in the previous sections. Once the rowset has been updated and resynchronized, it can be remoted back to the client, where the consumer application can check the status of updates or read new values obtained from the server.

Encoding Rowset Data with XML

OLE DB Rowsets are self-describing objects that contain data and metadata, created by executing commands against data stores. Because of its self-describing nature, rowset information can be easily represented in a descriptive and flexible language such as Microsoft® XML. XML is, therefore, supported as a streaming and persisting format for rowsets. This opens up the use of rowsets to non-COM platforms—if XML representing structured data can be generated from there, it can be translated into rowsets on other machines and vice versa. This also allows Recordset objects to be transmitted over HTTP as part of standard HTML text. The following is a subsection of an XML stream representing rowsets:

<?XML version="1.0" encoding="UTF-8" ?>
<orders>
<schema id="rss">
<elementType id="Customer" occurs="ONEORMORE">
      <element href="#NameEntry" occurs="ONEORMORE"></>
   <element href="#Order" occurs="ONEORMORE"></>
      <element href="#Date"></>
</elementType>
<elementType id="Date"> <pcdata></> </elementType>
<elementType id="NameEntry">
      <element href="#FirstName"></>
      <element href="#LastName"></>
</elementType>
<elementType id="LastName"> <pcdata></> </elementType>
<elementType id="FirstName"> <pcdata></> </elementType>
…
</schema>
<Customer>
   <NameEntry>
      <FirstName>Pierce</>
      <LastName>Bronson</>
   </NameEntry>
   <Order>
      <Name>Where the Sidewalk Ends</>
      <Author>Shel Silverstein</>
      <Price>$5.95</>
      <Detail>
         <Carrier>United Airlines</>
         <SKU>411AAA</>
      </Detail>
   </Order>
   …
   <Date>7/7/97</>
</Customer>
…
</orders>

Summary

The Universal Data Access architecture is designed to provide uniform access to the variety of data sources available in a corporation. In this platform, ADO, a provider-neutral and language-independent data access object model, works in conjunction with OLE DB and its service component architecture, which extends a provider’s native functionality transparently to the application and the provider. This architecture represents a new approach to extending database functionality dynamically at run time by aggregating OLE DB services using standard COM aggregation.

To date, there are OLE DB providers for most commercial relational DBMSs including Oracle, Microsoft® SQL Server™, IBM DB2, Sybase, Informix, CA-Ingres, and ODBC sources. In addition, there are providers for VSAM, AS-400 files, IMS/DB, and Microsoft® Windows NT® and Microsoft® Windows ® 2000 files via a content search engine. OLE DB has recently been extended to other domains such as OLAP, and spatial data. There are distributed query processors over OLE DB data from International Software Group, B2Systems, Intersolv, and Microsoft SQL Server. OLE DB, ADO, and all services described here are part of the Microsoft® Data Access 2.0 Components.


Learning ADO

Use the following sections to learn how to use ADO and RDS.

  • ADO and RDS Tutorials

  • Sample Applications for Remote Data Service

  • Remote Data Service Developer's Guide

  • ADO Code Examples


ADO and RDS Tutorials

  • ADO Tutorial

  • RDS tutorial. This tutorial illustrates how to use the RDS programming model to query and update a data source.

  • Building a Simple Remote Data Service Application. This tutorial walks you through building a simple Remote Data Service application that provides access to live data in a Microsoft® Access database.

  • Address Book. This tutorial shows you how to create a simple Remote Data Service application that provides access and updatability to a Microsoft® SQL Server™ database.


ADO Tutorial

This tutorial illustrates using the ADO programming model to query and update a data source. First, it describes the steps necessary to accomplish this task. Then, the tutorial is repeated in Microsoft® Visual Basic®; Microsoft® Visual C++®, featuring Visual C++ Extensions; Microsoft® Visual Basic® Scripting Edition (VBScript); and Microsoft® Visual J++®, featuring ADO for Windows Foundation Classes (ADO/WFC).

This tutorial is coded in different languages for two reasons:

  • The documentation for ADO assumes the reader codes in Visual Basic. This makes the documentation convenient for Visual Basic programmers, but less useful for programmers who use other languages.

  • If you are uncertain about a particular ADO feature and you know a little of another language, you may be able to resolve your question by looking for the same feature expressed in another language.

How the Tutorial is Presented

This tutorial is based on the ADO programming model. It discusses each step of the programming model individually. In addition, it illustrates each step with a fragment of Visual Basic code. At the end, it restates and integrates the code fragments as a Visual Basic example.

The code example is repeated in other languages, however, without the discussion. Each step in a given programming language tutorial is marked with the corresponding step in the programming model and descriptive tutorial. Use the number of the step to refer to the discussion in the descriptive tutorial.

Because this tutorial consists of several small fragments of code, you cannot execute the code as written.

The ADO programming model is restated below. Use it as a roadmap as you proceed through the tutorial.

ADO Programming Model with Objects

  • Make a connection to a data source (Connection). Optionally, begin a transaction.

  • Optionally, create an object to represent an SQL command (Command).

  • Optionally, specify columns, tables, and values in the SQL command as variable parameters (Parameter).

  • Execute the command (Command, Connection, or Recordset).

  • If the command is row-returning, store the rows in a storage object (Recordset).

  • Optionally, create a view of the storage object so you can sort, filter, and navigate the data (Recordset).

  • Edit the data, either adding, deleting, or changing rows and columns (Recordset).

  • If appropriate, update the data source with changes from the storage object (Recordset).

  • If a transaction was used, accept or reject the changes made during the transaction. End the transaction (Connection).

Next Step 1


Step 1: Open a Connection (ADO Tutorial)

You are Here...

  • Make a connection to a data source.

  • Optionally, create an object to represent an SQL query command.

  • Optionally, specify values in the SQL command as variable parameters.

  • Execute the command. If the command is row-returning, store the rows in a storage object.

  • Optionally, navigate, examine, manipulate, and edit the data.

  • If appropriate, update the data source with changes from the storage object. Optionally, embed the update in a transaction.

  • If you used a transaction, accept or reject the changes made during the transaction. End the transaction.

Discussion

You require a means to establish the conditions necessary to exchange data; that is, a connection. The data source you connect to is specified in a connection string, although the parameters specified in a connection string may differ for each provider and data source.

The main way ADO opens a connection is with the Connection.Open method. Alternatively, you can invoke the shortcut method, Recordset.Open, to both open a connection and issue a command over that connection in one operation. The following is the syntax for each method in Visual Basic:

connection.Open ConnectionString, UserID, Password, OpenOptions
recordset.Open Source, ActiveConnection, CursorType, LockType, Options

It's helpful to compare these two methods to highlight some useful characteristics of ADO method operands in general.

ADO provides several convenient ways to specify an operand. For example, Recordset.Open takes an ActiveConnection operand, which could be the literal string, a variable representing that string, or a Connection object representing an open connection.

Most methods on an object have properties that can provide an argument if a method operand is omitted. In the case of Connection.Open, you could omit the explicit ConnectionString operand and supply the information implicitly by setting the ConnectionString property to "DSN=pubs;uid=sa;pwd=;database=pubs".

Conversely, the uid and pwd keyword operands in a connection string can set the Connection object UserID and Password parameters.

This tutorial invokes the Connection.Open method with an explicit connection string. The data source will be the Open Database Connectivity (ODBC) pubs database, which ships as a test database with Microsoft SQL Server. (The actual location of the data source—such as a local drive or remote server—is specified when you define the Data Source Name (DSN).)

connection.Open "DSN=pubs;uid=sa;pwd=;database=pubs"

Next Step 2


Step 2: Create a Command (ADO Tutorial)

You are Here...

  • Make a connection to a data source.

  • Optionally, create an object to represent an SQL query command.

  • Optionally, specify values in the SQL command as variable parameters.

  • Execute the command. If the command is row-returning, store the rows in a storage object.

  • Optionally, navigate, examine, manipulate, and edit the data.

  • If appropriate, update the data source with changes from the storage object. Optionally, embed the update in a transaction.

  • If a transaction was used, accept or reject the changes made during the transaction. End the transaction.

Discussion

A query command requests that the data source return a Recordset object containing rows of requested information. Commands are typically written in SQL.

  1. As mentioned, operands such as a command string can be represented as:
    • A literal string or a variable that represents the string. This tutorial could query for all the information in the authors table of the pubs database with the command string "SELECT * from authors".

    • An object that represents the command string. In this case, the value of a Command object CommandText property set to the command string.
      Command cmd = New ADODB.Command;
cmd.CommandText = "SELECT * from authors"
  2. Specify a parameterized command string with the '?' placeholder.

    The content of an SQL string is fixed. However, you can create a parameterized command where '?' placeholder substrings can be replaced with parameters when a command is executed.

    You can optimize the performance of parameterized commands with the Prepared property. You can issue them repeatedly, changing only their parameters each time.

    For example, the following command string issues a query for all the authors whose last name is "Ringer":

    Command cmd = New ADODB.Command
cmd.CommandText = "SELECT * from authors WHERE au_lname = ?"
  3. Specify a Parameter object. Append it to the Parameters collection.

    Each '?' placeholder is replaced by the value of a corresponding Parameter object in the Command object Parameters collection. Create a Parameter object with Ringer as the value, then append it to the Parameters collection:

    Parameter prm = New ADODB.Parameter
prm.Name = "au_lname"
prm.Type = adVarChar
prm.Direction = adInput
prm.Size = 40
prm.Value = "Ringer"
cmd.Parameters.Append prm
  4. Specify and append a Parameter object with the CreateParameter method.

    ADO now offers a convenient alternative means to create a Parameter object and append it to the Parameters collection in one step.

    cmd.Parameters.Append cmd.CreateParameter _
                "au_lname", adVarChar, adInput, 40, "Ringer"

    However, this tutorial won't use a parameterized command, because you must use the Command.Execute method to substitute the parameters for the ‘?’ placeholders. But that method wouldn't allow us to specify Recordset cursor type and lock options. For that reason, use this code:

    Command cmd = New ADODB.Command;
cmd.CommandText = "SELECT * from authors"

For your information, this is the schema of the authors table:

Column Name | Data Type(length) | Nullable
au_id | ID (11) | no
au_lname | varchar(40) | no
au_fname | varchar(20) | no
Phone | char(12) | no
Address | varchar(40) | yes
City | varchar(20) | yes
State | char(2) | yes
Zip | char(5) | yes
Contract | bit | no

Next Step 3


Step 3: Execute the Command (ADO Tutorial)

You are Here...

  • Make a connection to a data source.

  • Optionally, create an object to represent an SQL query command.

  • Optionally, specify values in the SQL command as variable parameters.

  • Execute the command. If the command is row-returning, store the rows in a storage object.

  • Optionally, navigate, examine, manipulate, and edit the data.

  • If appropriate, update the data source with changes from the storage object. Optionally, embed the update in a transaction.

  • If a transaction was used, accept or reject the changes made during the transaction. End the transaction.

Discussion

The three methods that return a Recordset are Connection.Execute, Command.Execute, and Recordset.Open. This is their syntax in Visual Basic:

connection.Execute(CommandText, RecordsAffected, Options)
command.Execute(RecordsAffected, Parameters, Options)
recordset.Open Source, ActiveConnection, CursorType, LockType, Options

These methods are optimized to take advantage of the strengths of their particular objects.

Before you issue a command, you must open a connection. Each method that issues a command represents the connection differently:

  • The Connection.Execute method uses the connection embodied by the Connection object itself.

  • The Command.Execute method uses the Connection object set in its ActiveConnection property.

  • The Recordset.Open method specifies either a connect string or Connection object operand, or uses the Connection object set in its ActiveConnection property.

Another difference is the way the command is specified in the three methods:

  • In the Connection.Execute method, the command is a string.

  • In the Command.Execute method, the command isn't visible—it's specified in the Command.CommandText property. Furthermore, the command can contain parameter symbols (‘?’) which will be replaced by the corresponding parameter in the Parameters VARIANT array argument.

  • In the Recordset.Open method, the command is the Source argument, which can be a string or a Command object.

Each method trades off functionality versus performance:

  • The Execute methods are intended for—but are not limited to—executing commands that don't return data.

  • Both Execute methods return fast but read-only, forward-only Recordset objects.

  • The Command.Execute method allows you to use parameterized commands that can be reused efficiently.

  • On the other hand, the Open method allows you to specify the CursorType (strategy and object used to access the data); and LockType (specify the degree of isolation from other users, and whether the cursor should support updates in immediate or batch modes).

  • We advise you to study these options; they embody much of the functionality of a Recordset.

This tutorial uses a dynamic cursor to batch any changes to the Recordset. For this reason, use the following:

Recordset rs = New ADODB.Recordset
rs.Open cmd, conn, adOpenDymanic, adLockBatchOptimistic

Next Step 4


Step 4: Manipulate the Data (ADO Tutorial)

You are Here...

  • Make a connection to a data source.

  • Optionally, create an object to represent an SQL query command.

  • Optionally, specify values in the SQL command as variable parameters.

  • Execute the command. If the command is row-returning, store the rows in a storage object.

  • Optionally, navigate, examine, manipulate, and edit the data.

  • If appropriate, update the data source with changes from the storage object. Optionally, embed the update in a transaction.

  • If a transaction was used, accept or reject the changes made during the transaction. End the transaction.

Discussion

The bulk of the Recordset object methods and properties are devoted to examining, navigating, and manipulating the rows of Recordset data.

A Recordset can be thought of as an array of rows. The row you can examine and manipulate at any given time is the current row, and your location in the Recordset is the current row position. Every time you move to another row, that row becomes the new current row.

Several methods explicitly move or "navigate" through the Recordset (the Move methods). Some methods (the Find method) do so as a side effect of their operation. In addition, setting certain properties (Bookmark property) can also change your row position.

The Filter property can be applied to control rows you can access (that is, which rows are "visible" to you). The Sort property controls the order in which you navigate the rows of the Recordset.

A Recordset has a Fields collection, which is the set of Field objects that represent each field, or column, in a row. Assign or retrieve the data for a field from the Field object’s Value property. As an option, you can access field data in bulk (the GetRows and Update methods).

In this tutorial, you will:

  • Assume that telephone numbers in the "415" area code with exchanges starting with "5", are changing to the mythical area code "777."

  • Set the Optimize property on the Properties collection of the au_lname Field object to improve the performance of sorting and filtering.

  • Sort the Recordset on each author's last name.

  • Filter the Recordset so the only accessible (that is, "visible") rows will be those where the author's area code is "415" and exchange begins with "5".

Use the Move methods to navigate from the beginning of the sorted, filtered Recordset to the end. Stop when the Recordset EOF property indicates you've reached the last row. As you move through the Recordset, display the author's first and last name and the original telephone number, and then change the area code in the phone field to "777". (Telephone numbers in the phone field are of the form "aaa xxx-yyyy" where aaa is the area code and xxx is the exchange.)

rs("au_lname").Properties("Optimize") = TRUE
rs.Sort = "au_lname ASC"
rs.Filter = "phone LIKE '415 5*'"
rs.MoveFirst
Do While Not rs.EOF
   Debug.Print "Name: " & rs("au_fname") & " " rs("au_lname") & _
      "Phone: " rs("phone") & vbCr
   rs("phone") = "777" & Mid(rs("phone"), 5, 11)
   rs.MoveNext
Loop

Next Step 5


Step 5: Update the Data (ADO Tutorial)

You are Here...

  • Make a connection to a data source.

  • Optionally, create an object to represent an SQL query command.

  • Optionally, specify values in the SQL command as variable parameters.

  • Execute the command. If the command is row-returning, store the rows in a storage object.

  • Optionally, navigate, examine, manipulate, and edit the data.

  • If appropriate, update the data source with changes from the storage object. Optionally, embed the update in a transaction.

  • If a transaction was used, accept or reject the changes made during the transaction. End the transaction.

Discussion

You've just changed the data in several rows of the Recordset. ADO supports two basic concepts for the addition, deletion, and modification of rows of data.

The first notion is that changes aren't immediately made to the Recordset; instead, they are made to an internal copy buffer. If you decide you don't want the changes, then the modifications in the copy buffer are discarded. If you decide to keep the changes, then the changes in the copy buffer are applied to the Recordset.

The second notion is that changes are either propagated to the data source as soon as you declare the work on a row complete (that is, immediate mode), or all changes for a set of rows are collected until you declare that the work for the set is complete (that is, batch mode). These modes are governed by the CursorLocation and LockType properties.

In immediate mode, each invocation of the Update method propagates the changes to the data source. In batch mode, each invocation of Update or movement of the current row position saves the changes to the Recordset, but only the UpdateBatch method propagates the changes to the data source. You opened the Recordset in batch mode, so you'll update in batch mode.

Note There is a convenience form of Update in which you apply a change to a field, or an array of changes to an array of fields, then perform the update, all in one step.

Optionally, you can perform your update in a transaction. In practice, you would use a transaction to ensure that several related operations that depended on each other either all occurred successfully, or else were all canceled. In this case, a transaction isn't really necessary.

Transactions typically allocate and hold limited resources on the data source for long periods of time. For that reason it is advisable that a transaction exist for as brief a period as possible. (That's why this tutorial didn't begin the transaction as soon as you made a connection.)

For the tutorial, bracket your batch update in a transaction:

conn.BeginTrans
rs.UpdateBatch
...

Next Step 6


Step 6: Conclude the Update (ADO Tutorial)

You are Here...

  • Make a connection to a data source.

  • Optionally, create an object to represent an SQL query command.

  • Optionally, specify values in the SQL command as variable parameters.

  • Execute the command. If the command is row-returning, store the rows in a storage object.

  • Optionally, navigate, examine, manipulate, and edit the data.

  • If appropriate, update the data source with changes from the storage object. Optionally, embed the update in a transaction.

  • If a transaction was used, accept or reject the changes made during the transaction. End the transaction.

Discussion

Imagine that the batch update concluded with errors. How you resolve the errors depends on the nature and severity of the error and the logic of your application. However, if the database is shared with other users, one typical error is that someone else modifies the field before you do. This type of error is called a conflict. ADO detects this situation and reports an error.

This step in the tutorial has two parts: If there are no update errors, commit the transaction. This concludes the update.

If there are errors, they will be trapped in an error-handling routine. Filter the Recordset with the adFilterConflictingRecords constant so only the conflicting rows are visible. The error-resolution strategy is merely to print the author's first and last names (au_fname and au_lname). Then roll back the transaction, discarding the successful updates. This concludes the update.

...
conn.CommitTrans
...
On Error
rs.Filter = adFilterConflictingRecords
rs.MoveFirst
Do While Not rs.EOF
   Debug.Print "Conflict: Name: " & rs("au_fname") " " & rs("au_lname")
   rs.MoveNext
Loop
conn.Rollback
Resume Next
...

This is the end of the tutorial.


ADO Tutorial (VJ++)

This tutorial features the new ADO/WFC.

ASCimport com.ms.wfc.data.*;

/**
* ADOTutorial:
* Purpose: Demonstrates the usage of Ado in Java.
*          opens a recordset through a command object
*          and illustrates update within a transaction
*/
public class ADOTutorial
{
   public static String strConn = "Driver={SQL Server};SERVER=JDO_ODIN;DATABASE=JetQA;UID=testmod;PWD=testmod;";//odbc type conn string

   public static void main(String args[] )
   {
      try
      {
         Connection conn = new Connection();
         Command cmd = new Command();
         Recordset rs = new Recordset();

         int actErrorNum = 0;
         Field fld;
         AdoProperties fldProps;

// STEP1: Open the connection
         conn.open(strConn);

// STEP 2: Create a command
         cmd.setActiveConnection(conn);
         cmd.setCommandText("SELECT * from authors");

// Step 3: Open the recordset with the source as a command object
         rs.setCursorLocation(AdoEnums.CursorLocation.CLIENT);
         rs.setCursorType(AdoEnums.CursorType.DYNAMIC);
         rs.setLockType(AdoEnums.LockType.BATCHOPTIMISTIC);
         rs.open (cmd);

// STEP 4: Manipulate the data
         fldProps  = rs.getField("au_lname").getProperties();

                           fldProps.getItem("Optimize").setBoolean(true);

         rs.setSort("au_lname");

         rs.setFilter("phone like '415*'");

         rs.moveFirst();

         while ( !rs.getEOF())

         {
            StringBuffer strBuf = new StringBuffer();
            System.out.println( " Name: " + rs.getField("au_fname").getString() +
               "  " + rs.getField("au_lname").getString() +
               "   Phone : " + rs.getField("phone").getString() );

            //change the area code 415 to 779 for the field
            fld =  rs.getField("phone");

            strBuf.append( fld.getString());

            strBuf.setCharAt(0, '7');
            strBuf.setCharAt(1, '7');
            strBuf.setCharAt(2,  '9');

            //set the field to the new value
            fld.setString(strBuf.toString());

            rs.moveNext();

         }

// STEP5: Update the set field values
         conn.beginTrans();

                 //STEP6 Part A: Conclude the Update
         try
         {

            rs.updateBatch();
            conn.commitTrans();

         }
// STEP6: Conclude the Update

         catch(com.ms.wfc.data.AdoException ex)
         {

           //error has occured we have to rollback
           //transaction
            rs.setFilter(new Integer                        (AdoEnums.FilterGroup.CONFLICTINGRECORDS));
            rs.moveFirst();

            while(!rs.getEOF())
            {
               //print conflicting records
               System.out.println(" Conflict : Name : "+ rs.getField("au_fname").getString() + " " +
                  rs.getField("au_lname").getString() );

               rs.moveNext();
            }
            conn.rollbackTrans();
         }
             System.out.println("type any character to continue...");
         System.in.read();

      }
      catch(Exception ex)
      {
         ex.printStackTrace();
      }
   }
}

This is the end of the Visual J++ tutorial.


RDS Tutorial

This tutorial illustrates using the RDS programming model to query and update a data source. First, it describes the steps necessary to accomplish this task. Then the tutorial is repeated in Microsoft® Visual Basic® Scripting Edition and Microsoft® Visual J++®, featuring ADO for Windows Foundation Classes (ADO/WFC).

This tutorial is coded in different languages for two reasons:

  • The documentation for RDS assumes the reader codes in Visual Basic. This makes the documentation convenient for Visual Basic programmers, but less useful for programmers who use other languages.

  • If you are uncertain about a particular RDS feature and you know a little of another language, you may be able to resolve your question by looking for the same feature expressed in another language.

How the Tutorial is Presented

This tutorial is based on the RDS programming model. It discusses each step of the programming model individually. In addition, it illustrates each step with a fragment of Visual Basic code.

The code example is repeated in other languages with minimal discussion. Each step in a given programming language tutorial is marked with the corresponding step in the programming model and descriptive tutorial. Use the number of the step to refer to the discussion in the descriptive tutorial.

The RDS programming model is stated below. Use it as a roadmap as you proceed through the tutorial.

RDS Programming Model with Objects

  • Specify the program to be invoked on the server, and obtain a way (proxy) to refer to it from the client.

  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue

  • The server program obtains a Recordset object from the data source, typically by using ADO. Optionally, the Recordset object is processed on the server.

  • The server program returns the final Recordset object to the client application.

  • On the client, the Recordset object is optionally put into a form that can be easily used by visual controls.

  • Changes to the Recordset object are sent back to the server and used to update the data source.

Next Step 1


Step 1: Specify a Server Program (RDS Tutorial)

You are Here...

  • Specify the program to be invoked on the server, and obtain a proxy.

  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue.

  • The server program obtains a Recordset object from the data source, typically by using ADO.

  • The server program returns the final Recordset object to the client application.

  • On the client, the Recordset object is optionally put into a form that can be easily used by visual controls.

  • Changes to the Recordset object are sent back to the server and used to update the data source.

Discussion

In the most general case, use the RDS.DataSpace object CreateObject method to specify the default server program, RDSServer.DataFactory, or your own custom server program (business object). A server program is instantiated on the server, and a reference to the server program, or proxy, is returned.

This tutorial uses the default server program:

Sub RDSTutorial1()
Dim DS as New RDS.DataSpace
Dim DF as Object
Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
...

Next Step 2


Step 2: Invoke the Server Program (RDS Tutorial)

You are Here...

  • Specify the program to be invoked on the server, and obtain a proxy.

  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue.

  • The server program obtains a Recordset object from the data source, typically by using ADO.

  • The server program returns the final Recordset object to the client application.

  • On the client, the Recordset object is optionally put into a form that can be easily used by visual controls.

  • Changes to the Recordset object are sent back to the server and used to update the data source.

Discussion

When you invoke a method on the client proxy, the actual program on the server executes the method. In this step, you'll execute a query on the server.

Part A If you weren't using RDSServer.DataFactory in this tutorial, the most convenient way to perform this step would be to use the RDS.DataControl object. The RDS.DataControl combines the previous step of creating a proxy, and this step, issuing the query.

Set the RDS.DataControl object Server property to identify where the server program should be instantiated; the Connect property to specify the connect string to access the data source; and the SQL property to specify the query command text. Then issue the Refresh method to cause the server program to connect to the data source, retrieve rows specified by the query, and return a Recordset object to the client.

This tutorial doesn't use the RDS.DataControl, but this is how it would look if it did:

Sub RDSTutorial2A()
Dim DC as New RDS.DataControl
DC.Server = "http://yourServer"
DC.Connect = "DSN=pubs"
DC.SQL = "SELECT * FROM authors"
DC.Refresh
...

Nor does the tutorial invoke RDS with ADO objects, but this is how it would look if it did:

Dim rs as New ADODB.Recordset
rs.Open "SELECT * FROM authors", "Provider=MS Remote;Data Source=pubs;Remote Server=http://YourServer"

Part B The general way of performing this step is to invoke the RDSServer.DataFactory object Query method. That method takes a connect string, which is used to connect to a data source, and command text, which is used to specify the rows to be returned from the data source.

This tutorial uses the RDSServer.DataFactory Query method:

Sub RDSTutorial2B()
Dim DS as New RDS.DataSpace
Dim DF
Dim RS as ADODB.Recordset
Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
Set RS = DF.Query ("DSN=pubs", "SELECT * FROM authors")
...

Next Step 3


Step 3: Server Obtains a Recordset (RDS Tutorial)

You are Here...

  • Specify the program to be invoked on the server, and obtain a proxy.

  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue.

  • The server program obtains a Recordset object from the data source, typically by using ADO.

  • The server program returns the final Recordset object to the client application.

  • On the client, the Recordset object is optionally put into a form that can be easily used by visual controls.

  • Changes to the Recordset object are sent back to the server and used to update the data source.

Discussion

The server program uses the connect string and command text to query the data source for the desired rows. ADO is typically used to retrieve this Recordset, although other Microsoft data access interfaces, such as OLE DB, could be used. See the ADO Tutorial for a detailed explanation of how the query is performed.

A custom server program might look like this:

Public Function ServerProgram(conn as String, qry as String) as Object
Dim rs as New ADODB.Recordset
rs.CursorLocation = adUseClient
rs.Open cn, qry, adOpenUnspecified, adLockUnspecified, _
  adCmdUnspecified
Set ServerProgram = rs
End Function

Next Step 4


Step 4: Server Returns the Recordset (RDS Tutorial)

You are Here...

  • Specify the program to be invoked on the server, and obtain a proxy.

  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue.

  • The server program obtains a Recordset object from the data source, typically by using ADO.

  • The server program returns the final Recordset object to the client application.

  • On the client, the Recordset object is optionally put into a form that can be easily used by visual controls.

  • Changes to the Recordset object are sent back to the server and used to update the data source.

Discussion

RDS converts the retrieved Recordset object to a form that can be sent back to the client (that is, it marshals the Recordset). The exact form of the conversion and how it is sent depends on whether the server is on the Internet or an intranet, a local area network, or is a dynamic-link library. However, this detail isn't critical; all that matters is that RDS sends the Recordset back to the client.

On the client side, a Recordset object is returned and assigned to a local variable.

Sub RDSTutorial4()
Dim DS as New RDS.DataSpace
Dim RS as New ADODB.Recordset   'Optionally, ADOR.Recordset
Dim DF as Object
Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
Set RS = DF.Query("DSN=pubs", "SELECT * FROM authors")
...

Next Step 5


Step 5: DataControl is made usable (RDS Tutorial)

You are Here...

  • Specify the program to be invoked on the server, and obtain a proxy.

  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue.

  • The server program obtains a Recordset object from the data source, typically by using ADO.

  • The server program returns the final Recordset object to the client application.

  • On the client, the Recordset object is optionally put into a form that can be easily used by visual controls.

  • Changes to the Recordset object are sent back to the server and used to update the data source.

Discussion

The returned Recordset object is available for use. You can examine, navigate, or edit it as you would any other recordset. What you can do with the recordset depends on your environment. Microsoft Visual Basic and Visual C++ have visual controls that can use a Recordset directly, or indirectly with the aid of an enabling data control.

For example, if you are displaying a Web page in Microsoft® Internet Explorer, you might want to display the Recordset object data in a visual control. Visual controls on a Web page cannot access a Recordset object directly. However, they can access (that is, bind to) the Recordset object through an RDS.DataControl. The RDS.DataControl becomes usable by a visual control when its SourceRecordset property is set to the Recordset object.

(The visual control object must have its DATASRC parameter set to the RDS.DataControl, and its DATAFLD property set to a Recordset object field (column).)

In this tutorial, set the SourceRecordset property.

Sub RDSTutorial5()
Dim DS as New RDS.DataSpace
Dim RS as New ADODB.Recordset   'Optionally, ADOR.Recordset
Dim DC as New RDS.DataControl
Dim DF as Object
Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
Set RS = DF.Query ("DSN=pubs", "SELECT * FROM authors")
DC.SourceRecordset = RS         'Visual controls can now bind to DC.
...

Next Step 6


Step 6: Changes are Sent to the Server (RDS Tutorial)

You are Here...

  • Specify the program to be invoked on the server, and obtain a proxy.

  • Invoke the server program. Pass parameters to the server program that identifies the data source and the command to issue.

  • The server program obtains a Recordset object from the data source, typically by using ADO.

  • The server program returns the final Recordset object to the client application.

  • On the client, the Recordset object is optionally put into a form that can be easily used by visual controls.

  • Changes to the Recordset object are sent back to the server and used to update the data source.

Discussion

If the Recordset object is edited, any changes (that is, rows that are added, changed, or deleted) can be sent back to the server.

Note The default behavior of RDS can be invoked implicitly with ADO objects and the Microsoft OLE DB Remoting Provider. Queries can return recordsets, and edited recordsets can update the data source. This tutorial doesn't invoke RDS with ADO objects, but this is how it would look if it did:

Dim rs as New ADODB.Recordset
rs.Open "SELECT * FROM authors", "Provider=MS Remote;Data Source=pubs;Remote Server=http://YourServer"
...            'Edit the recordset
rs.UpdateBatch    'The equivalent of SubmitChanges
...

Part A Assume for this case that you have only used the RDS.DataControl and that a Recordset object is now associated with the RDS.DataControl. The SubmitChanges method updates the data source with any changes to the Recordset object if the Server and Connect properties are still set.

Sub RDSTutorial6A()
Dim DC as New RDS.DataControl
Dim RS as New ADODB.Recordset   'Optionally, ADOR.Recordset
DC.Server = "http://yourServer"
DC.Connect = "DSN=pubs"
DC.SQL = "SELECT * FROM authors"
DC.Refresh
...
Set RS = DC.Recordset
...                           'Edit the Recordset
...
DC.SubmitChanges
...

Part B Alternatively, you could update the server with the RDSServer.DataFactory object, specifying a connection and a Recordset object.

Sub RDSTutorial6B()
Dim DS as New RDS.DataSpace
Dim RS as New ADODB.Recordset   'Optionally, ADOR.Recordset
Dim DC as New RDS.DataControl
Dim DF as Object
Set DF = DS.CreateObject("RDSServer.DataFactory", "http://yourServer")
Set RS = DF.Query ("DSN=pubs", "SELECT * FROM authors")
DC.SourceRecordset = RS         'Visual controls can now bind to DC.
...                           'Edit the Recordset
blnStatus = DF.SubmitChanges "DSN=pubs", RS

This is the end of the tutorial.


RDS Tutorial (VJ++)

ADO/WFC doesn't completely follow the RDS object model in that it does not implement the DataControl object. ADO/WFC only implements the client-side class, DataSpace.

The DataSpace class implements one method, createObject, which returns an ObjectProxy object. The DataSpace class also implements the InternetTimeout property.

The ObjectProxy class implements one method, call, which can invoke any server-side business object.

This is the beginning of the tutorial.

import com.ms.wfc.data.*;
public class RDSTutorial
{
    public void tutorial()
    {
// Step 1: Specify a server program
        ObjectProxy obj =
            DataSpace.createObject(
                "RDSServer.DataFactory",
                "http://YourServer");

// Step 2: Server returns a Recordset
        Recordset rs = (Recordset) obj.call(
                "Query",
                new Object[] {"DSN=pubs", "SELECT * FROM authors"});

// Step 3: Changes are sent to the server
        ...                                // Edit Recordset
        obj.call(
                "SubmitChanges",
                new Object[] {"DSN=pubs", rs});
        return;
    }
}

This is the end of the tutorial.


Integrating Java with MTS

Microsoft® Transaction Server (MTS) is a component-based model and runtime environment that simplifies the task of developing application components by allowing you to perform work using transactions. Transactions are multiple interactions that are treated as a single operation. Intermediate steps are not actually taken until all interactions are completed successfully. If any interaction fails, all steps are returned to their original setting.

These application components implement business rules and run from the middle tier in a three-tier application. For example, in the case of a bank, records in one or more database systems represent the durable state of the business, such as the amount of money in an account. The application components update that state to reflect changes, such as debits and credits.

MTS application components—the business logic that runs on the server—can be written with any language that produces Microsoft® ActiveX® dynamic-link libraries (DLLs).

MTS support for Java is of particular interest to Internet programmers because all MTS middle-tier services are provided to ActiveX components written in Java. All MTS server functionality is available for developers building applications in Java. MTS acts as a transaction-processing monitor for Java.

The following MTS documents are provided.

White Papers

Microsoft Transaction Server Transactional Component Services explains the problems MTS addresses and why it is useful in middle-tier development.

Comparing Microsoft® Transaction Server to Enterprise JavaBeans is an overview of transaction systems, showing how each area works as compared with Enterprise JavaBeans.

Writing Microsoft Transaction Server Components in Java is an overview of the MTS development process. It gives step-by-step instruction on building a simple component, shows two client applications, and runs the component under MTS. It also provides some debugging tips.

Writing Microsoft® Transaction Server Components in Java with Microsoft® Visual J++® 6.0 explains how to create a Java MTS component and a client application, and how to debug the server-side component.

Building MTS Applications covers the packaging, calling, debugging, and automating of MTS components, and shows how to develop MTS components with Java.

Managing In-Process COM Components Using MTS and ASP Technology presents a solution for managing in-process COM components using MTS, a sample Microsoft® Visual Basic® component, and ASP technology.

IIS 4.0 and MTS 2.0: Technology for the Web explains why MTS was integrated into the IIS architecture, describes the three major benefits of this integration, and shows how to develop server-side components that use MTS.

Sample

The MTS Sample Bank Application demonstrates building MTS components with Java. The sample is in %SDKDIR%\Samples\MTS.

Package Reference

The com.ms.mtx package contains eight classes for Java/MTS integration.


Reference (Microsoft Transaction Server)

MTS Reference

Visual J++

Interfaces

Methods

Language Summary


Interfaces (Visual J++)

You use the following object in base clients:

ITransactionContext

You use the following objects in components:

IGetContextProperties

IObjectContext

IObjectControl

ISharedProperty

ISharedPropertyGroup

ISharedPropertyGroupManager


Methods (Visual J++)

Abort

Activate

CanBePooled

Commit

Count

CreateInstance

CreateInstance (Transaction Context)

CreatePropertyGroup

CreateProperty

CreatePropertyByPosition

Deactivate

DisableCommit

EnableCommit

EnumNames

get_NewEnum

getGroup

getProperty

GetProperty

getPropertyByPosition

getValue

IsCallerInRole

IsInTransaction

IsSecurityEnabled

MTx.GetObjectContext

MTx.SafeRef

putValue

SetAbort

SetComplete


Language Summary (Visual J++)

Context Class

Context Class

Context Methods

createInstance

disableCommit

enableCommit

getPropertyNames

getProperty

isCallerInRole

isInTransaction

isSecurityEnabled

setAbort

setComplete

Mtx.GetObjectContext

MTx.GetObjectContext

MTx.GetObjectContext Method

MTx.SafeRef Method

IGetContextProperties Interface

IGetContextProperties Interface

IGetContextProperties Methods

Count

EnumNames

GetProperty

IObjectContext Interface

IObjectContext Interface

IObjectContext Methods

CreateInstance

DisableCommit

EnableCommit

IsCallerInRole

IsInTransaction

IsSecurityEnabled

SetAbort

SetComplete

IObjectControl Interface

IObjectControl Interface

IObjectControl Methods

Activate

CanBePooled

Deactivate

ISharedProperty Interface

ISharedProperty Interface

ISharedProperty Methods

putValue

getValue

ISharedPropertyGroup Interface

ISharedPropertyGroup Interface

ISharedPropertyGroup Methods

CreateProperty

CreatePropertyByPosition

getProperty

getPropertyByPosition

ISharedPropertyGroupManager Interface

ISharedPropertyGroupManager Interface

ISharedPropertyGroupManager Methods

CreatePropertyGroup

getGroup

get_NewEnum

ITransactionContextEx Interface

ITransactionContextEx Interface

ITransactionContextEx Methods

Abort

Commit

CreateInstance


Context Class (Visual J++)

Context Class

The Context class provides access to the current object's context.

Remarks

The Context class is in the package com.ms.mtx.

You can use an object's Context to:

  • Declare that the object's work is complete.

  • Prevent a transaction from being committed, either temporarily or permanently.

  • Instantiate other MTS objects and include their work within the scope of the current object's transaction.

  • Find out if a caller is in a particular role.

  • Find out if security is enabled.

Find out if the object is executing within a transaction.

The Context class exposes the following methods.

Method | Description
createObject | 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.
getProperty | Returns a context object property.
getPropertyNames | Returns the names of the context object properties.
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.
getSafeRef | Used by an object to obtain a reference to itself that's safe to pass outside its context.
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.
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.

Note When an object calls disableCommit, enableCommit, setComplete, or setAbort from within a method, two flags (Done and Consistent) are set in its Context. (See the following table for an explanation.) These flags aren't evaluated by the MTS run-time environment until the object's currently executing method returns to its caller. This means that an object can call these methods any number of times from within one of its own methods, but the last call before the object returns to its client is the one that will be in effect.

Method | Done | Consistent
setAbort | TRUE | FALSE
setComplete | TRUE | TRUE
disableCommit | FALSE | FALSE
enableCommit | FALSE | TRUE

The Done flag, which allows an object to be deactivated and its transaction to commit or abort, is only evaluated after the object returns from the call that first entered its context. For example, suppose client A calls into object B. Object B calls setComplete and then calls into object C (passing it a safe reference for a callback). Object C calls back to object B, and then object B returns to client A. Object B won't be deactivated when it returns to object C; it will be deactivated when it returns to client A.

See Also

Basic Security Methods, Passing Object References, Context Objects, Transactions, Deactivating Objects


Context.createObjectMethod (Visual J++)

createObject Method

Instantiates an MTS object.

Provided By

Context class

IUnknown createObject (String progID);

IUnknown createObject (com.ms.com._Guid rclsid)

Parameters

progID

[in] The programmatic identifier (ProgID) of the object to be created.

rclsid

[in] Reference to the class identifier (CLSID) of the object to be created.

Return Value

A reference to the IUnknown interface on the newly created object.

Remarks

createObject creates a COM object. However, the object will have context only if its component is registered with MTS.

When you create an object by using createObject, the new object's context is derived from the current object's Context and the declarative properties of the new object's component. The new object always executes within the same activity as the object that created it. If the current object has a transaction, the transaction attribute of the new object's component determines whether or not the new object will execute within the scope of that transaction.

If the component's transaction attribute is set to either Requires a transaction or Supports transactions, the new object inherits its creator's transaction. If the component's transaction attribute is set to Requires a new transaction, MTS initiates a new transaction for the new object. If the component's transaction attribute is set to Does not support transactions, the new object doesn't execute under any transaction.

MTS always uses standard marshaling. Even if a component exposes the IMarshal interface, its IMarshal methods will never be called by the MTS run-time environment.

You can't create MTS objects as part of an aggregation.

See Also

Creating MTS Objects, Transaction Attributes, MTS Component Requirements


Context.disableCommit Method (Visual J++)

disableCommit Method

Declares that the object's transactional updates are inconsistent and can't be committed in their present state.

Provided By

Context class

void disableCommit ( );

Remarks

An object that invokes disableCommit is stateful.

You can use the disableCommit method to prevent a transaction from committing prematurely between method calls in a stateful object. When an object invokes disableCommit, it indicates that its work is inconsistent and that it can't complete its work until it receives further method invocations from the client. It also indicates that it needs to maintain its state to perform that work. This prevents the MTS run-time 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.

For example, suppose you have a General Ledger component that updates a database. A client makes multiple calls to a General Ledger object to post entries to various accounts. There's an integrity constraint that says the debits must equal the credits when the final method invocation returns, or the transaction must abort. The General Ledger object has an initialization method in which the client informs it of the sequence of calls the client is going to make, and the General Ledger object calls disableCommit. The object maintains its state between calls so that after the final call in the sequence is made the object can make sure the integrity constraint is satisfied before allowing its work to be committed.

See Also

Transactions


Context.enableCommit Method (Visual J++)

enableCommit Method

Declares that the current object's work is not necessarily finished, but that its transactional updates are consistent and could be committed in their present form.

Provided By

Context class

void enableCommit ( );

Remarks

When an object calls enableCommit, it allows the transaction in which it's 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.

See Also

Transactions


Context.getDirectCallerName Method (Visual J++)

getDirectCallerName Method

Retrieves the user name associated with the external process that called the currently executing method.

String getDirectCallerName ( );

Provided By

Context class

Return Values

User name associated with the process from which the current method was invoked.

Remarks

You use the getDirectCallerName method to determine the user name associated with the process that called the object's currently executing method. The following scenarios illustrate the functionality of the getDirectCallerName method.

A base process running on server A, as user A, calls into object X on server B, running as user B. Then object X calls into object Y, running on server C. If object Y calls getDirectCaller, the name of user B is returned.

Security can only be enforced across process boundaries. This means that the name returned by getDirectCallerName is the name associated with the process that called into the process in which the current object is running, not necessarily the immediate caller into the object itself. If an object calls into another object within the same process, when the second object calls getDirectCallerName, it will get the name of the most immediate caller outside its own process boundary, not the name of the object that directly called into it.

A base process, running on server A as user A, calls into object X on server B, running as user B. Then object X calls into object Y, running in the same process as object X, also on server B. When object Y calls getDirectCallerName, the name of user A is returned , not the name of user B.

See Also

Programmatic Security, Advanced Security Methods, Context class


Context.getDirectCreatorName Method (Visual J++)

getDirectCreatorName Method

Retrieves the user name associated with the current object's immediate (out-of-process) creator.

Provided By

Context class

String getDirectCreatorName ( );

Return Values

User name associated with the process that directly created the current object.

Remarks

You use the getDirectCreatorName method to determine the the process that created the current object. The following scenarios illustrate the functionality of the getDirectCreatorName method.

A base process running on server A, as user A, creates object X on server B, running as user B. Then object X creates object Y, running on server C. If object Y calls getDirectCreatorName, the name of user B is returned.

Security can only be enforced across process boundaries. This means that if an object creates another object within the same process, when the second object calls getDirectCreatorName, it will get the of the most immediate creator outside its own process boundary, not the user name associated with the object that actually created it.

A base client running on server A, as user A, creates object X on server B, running as user B. Then object X creates object Y, running in the same process as object X, also on server B. When object Y calls getDirectCreatorName, the name of user A is returned, not the name of user B.

See Also

Programmatic Security, Advanced Security Methods, Context class


Context.getObjectContext Method (Visual J++)

getObjectContext Method

Obtains a reference to the ObjectContext that's associated with the current MTS object.

getObjectContext is a static method of the Context class, which is declared in the package com.ms.mtx.

Note The class Context has only static methods and has no public constructor. You can't create an instance of this class.

Return Value

A reference to the IObjectContext interface on the current object's context. getObjectContext will return null if it is called from a constructor or finalizer, or if the object's component hasn't been imported into an MTS package.

Remarks

An object should never attempt to pass its ObjectContext reference to another object. If you pass an ObjectContext reference to another object, it will no longer be a valid reference.

See Also

Building Scalable Components, Context Objects


Context.getOriginalCallerName Method (Visual J++)

getOriginalCallerName Method

Retrieves the user name associated with the base process that initiated the sequence of calls from which the call into the current object originated.

Provided By

Context class

String getOriginalCallerName ( );

Return Values

User name associated with the the base process that initiated the call sequence from which the current method was called.

Remarks

You use the getOriginalCallerName method to determine the user name associated with the original process that initiated the call sequence from which the current method was called. The following scenario illustrates the functionality of the getOriginalCallerName method.

Base process 1, running on server A as user A, creates object X on server B, running as user B. Then base process 1 passes its reference on object X to base process 2, running on server D as user D. Base process 2 uses that reference to call into object X. object X then calls into object Y, running on server C. If object Y then calls getOriginalCallerName, the name of user D is returned.

Note Usually, an object's original caller is the same process as its original creator. The only situation in which the original caller and the original creator would be different is one in which the original creator passes a reference to another process, and the other process initiates the call sequence (as in the preceding example).

Note The path to the original caller is broken if any object along the chain was created by some other means than IObjectContext::CreateInstance or ITransactionContext::CreateInstance. For example, if base process 1 uses CoCreateInstance to create X, when Y calls getOriginalCallerName, the name it gets back will be the name of user B, not user D. This is because the call sequence is traced back through the objects' context and MTS can only create a context for an object that's created with either IObjectContext::CreateInstance or ITransactionContext::CreateInstance.

See Also

Programmatic Security, Advanced Security Methods, Context class


Context.getOriginalCreatorNameMethod (Visual J++)

getOriginalCreatorName Method

Retrieves the user name associated with the original base process that initiated the activity in which the current object is executing.

Provided By

Context class

String getOriginalCreatorName ( );

Return Values

User name associated with the the base process that initiated the activity in which the current object is executing.

Remarks

You use the getOriginalCreatorName method to determine the the process that initiated the activity in which the current object is executing. The following scenario illustrates the functionality of the getOriginalCreatorName method.

A base process running on server A, as user A, creates object X on server B, running as user B. Then object X creates object Y, running on server C. If object Y calls getOriginalCreatorName, the name of user A is returned.

Note The path to the original creator is broken if an object is created by some other means than IObjectContext::CreateInstance or ITransactionContext::CreateInstance. For example, if the base process on server A uses CoCreateInstance to create X, when Y calls getOriginalCreatorName, the name it gets back will be the name of user B, not user A. This is because the creation sequence is traced back through the objects' context and MTS can only create a context for an object that's created with either IObjectContext::CreateInstance or ITransactionContext::CreateInstance.

See Also

Programmatic Security, Advanced Security Methods, Context class


Context.getProperty Method (Visual J++)

getProperty Method

Returns a context object property.

Provided By

Context class

Variant getProperty(
String propertyName);

Parameters

propertyName

[in] The name of the context object property to be retrieved.

Return Value

The requested context object property.

Remarks

You can use getProperty to retrieve the following Microsoft Internet Information Server (IIS) intrinsic objects:

  • Request

  • Response

  • Server

  • Application

  • Session

For more information, see the IIS documentation.

To retrieve an IIS object, use the getDispatch method of returned Variant and cast this value to the interface to the IIS object (for example, IReponse for the Response object).

Note Context properties are not available in MTS 1.0 server processes.


Context.getPropertyNames Method (Visual J++)

getPropertyNames Method

Returns the names of the context object properties.

Provided By

Context class

String[] getPropertyNames ( );

Return Value

An array of context object property names.


Context.getSafeRef Method (Visual J++)

getSafeRef Method

Used by an object to obtain a reference to itself that's safe to pass outside its context.

getSafeRef is a static method of the Context class, which is declared in the package com.ms.mtx.

Note The class Context has only static methods and has no public constructor. You can't create an instance of this class.

Provided By

Context class

IUnknown getSafeRef (

IUnknown unknown

);

Parameter

unknown

[in] A reference to the current object.

Return Value

A reference to the current object that's safe to pass outside the current object's context.

Remarks

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 getSafeRef first and then pass the reference returned by this call. An object should never pass a reference to to a client or to any other object. Once such a reference is passed outside the object's context, it's no longer a valid reference.

Calling getSafeRef on a reference that is already safe returns the safe reference unchanged.

See Also

Passing Object References


Context.isCallerInRole Method (Visual J++)

isCallerInRole Method

Indicates whether an object's direct caller is in a specified role (either individually or as part of a group).

Provided By

Context class

boolean isCallerInRole (

String role

);

Parameters

role

[in] The name of the role in which you want to determine whether the caller is acting.

Return Values

true

Either the caller is in the specified role, or security is not enabled.

false

The caller is not in the specified role.

Remarks

You use this method to determine whether the direct caller of the currently executing method is associated with a specific role. A role is a symbolic name that represents a user or group of users who have specific access permissions to all components in a given package. Developers define roles when they create a component, and roles are mapped to individual users or groups at deployment time.

isCallerInRole only applies to the direct caller of the currently executing method. (The direct caller is the process calling into the current server process. It can be either a base client process or a server process.) isCallerInRole doesn't apply to the process that initiated the call sequence from which the current method was called, or to any other callers in that sequence.

Because isCallerInRole returns true when the object that invokes it is executing in a client's process, it's a good idea to call isSecurityEnabled before calling isCallerInRole. If security isn't enabled, isCallerInRole won't return an accurate result.

See Also

Programmatic Security, Basic Security Methods, Secured Components


Context.isInTransaction Method (Visual J++)

isInTransaction Method

Indicates whether the current object is executing in a transaction.

Provided By

Context class

boolean isInTransaction ( );

Return Values

true

The current object is executing within a transaction.

false

The current object is not executing within a transaction.

Remarks

You can use this method to make sure that an object that requires a transaction never runs without one. For example, if a component that requires a transaction is improperly configured in the MTS Explorer, you can use this method to determine that the object doesn't have a transaction. Then you can return an error to alert the user to the problem, or take whatever action is appropriate.

See Also

Transaction Attributes, Transactions


Context.isSecurityEnabled Method (Visual J++)

isSecurityEnabled Method

Indicates whether or not security is enabled for the current object. MTS security is enabled unless the object is running in the client's process.

Provided By

Context class

boolean isSecurityEnabled ( );

Return Values

true

Security is enabled for this object.

false

Security is not enabled for this object.

Remarks

MTS security is enabled only if an object is running in a server process. This could be either because the object's component was configured to run in a client's process, or because the component and the client are in the same package. If the object is running in the client's process, there is no security checking and isSecurityEnabled will always return false.

See Also

Programmatic Security, Basic Security Methods, Secured Components


Context.setAbort Method (Visual J++)

setAbort Method

Declares that the transaction in which the object is executing must be aborted, and that the object should be deactivated on returning from the currently executing method call.

Provided By

Context class

void setAbort ( );

Remarks

The object is deactivated automatically on return from the method in which it called setAbort. If the object is the root of an automatic transaction, MTS aborts the transaction. If the object is transactional, but not the root of an automatic transaction, the transaction in which it's participating is doomed to abort. (An object is the root of a transaction if the MTS run-time environment has to initiate a new transaction for it. This is the case when the component that provides the object is configured to require a transaction and the object's creator doesn't have one, or when the component is configured to require a new transaction.)

You can call setAbort in error handlers to ensure that a transaction aborts when an error occurs. You can also call setAbort at the beginning of a method to protect your object from committing prematurely in the event of an unexpected return and then call setComplete just before the method returns, if all goes well.

See Also

Transactions, Context Objects, Deactivating Objects


Context.setComplete Method (Visual J++)

setComplete Method

Declares that the current object has completed its work and should be deactivated when the currently executing method returns to the client. For objects that are executing within the scope of a transaction, it also indicates that the object's transactional updates can be committed.

Provided By

Context class

void setComplete ( );

Remarks

The object is deactivated automatically on return from the method in which it called setComplete. If the object is the root of an automatic transaction, MTS attempts to commit the transaction. However, if any object that was participating in the transaction has called setAbort, or has called disableCommit and has not subsequently called enableCommit or setComplete, the transaction will be aborted. (An object is the root of a transaction if the MTS run-time environment has to initiate a new transaction for it. This is the case when the component that provides the object is configured to require a transaction and the object's creator doesn't have one, or when the component is configured to require a new transaction.)

If an object doesn't need to maintain its state after it returns from a method call, it should call setComplete so that it can be automatically deactivated as soon as it returns and its resources can be reclaimed.

See Also

Transactions, Context Objects, Deactivating Objects


IGetContextProperties Interface (Visual J++)

IGetContextProperties Interface

The IGetContextProperties interface provides access to context object properties.

Remarks

New applications should use the Context class.

The IGetContextProperties interface is declared in the package com.ms.mtx.

The IGetContextProperties interface exposes the following methods.

Method | Description
Count | Returns the number of properties associated with the context object.
EnumNames | Returns a reference to an enumerator that you can use to iterate through all the context object properties.
GetProperty | Returns a context object property.

See Also

Context Objects


IGetContextProperties.Count Method (Visual J++)

IGetContextProperties.Count Method

Returns the number of context object properties.

Provided By

IGetContextProperties

int Count( );

Return Value

The number of properties.

Example


IGetContextProperties.EnumNames Method (Visual J++)

IGetContextProperties.EnumNames Method

Returns a reference to an enumerator that you can use to iterate through all the context object properties.

New applications should call getPropertyNames.

Provided By

IGetContextProperties

IEnumNames EnumNames ( );

Return Value

A reference to the IEnumNames interface on a new enumerator object that you can use to iterate through the list of all the shared property groups in the process.

Remarks

You use the EnumNames method to obtain a reference to an enumerator object. The returned IEnumNames interface exposes several methods you can use to iterate through a list of string expressions representing context object properties. Once you have a name, you can use the GetProperty method to obtain a reference to the context object property it represents. See the COM documentation for information on enumerators.

Example


IGetContextProperties.GetProperty Method (Visual J++)

IGetContextProperties.GetProperty Method

Returns a context object property.

New applications should call getProperty.

Provided By

IGetContextProperties

Variant GetProperty(
String name);

Parameters

name

[in] The name of the context object property to be retrieved.

Return Value

The requested context object property.

Remarks

You can use GetProperty to retrieve the following Microsoft Internet Information Server (IIS) intrinsic objects:

  • Request

  • Response

  • Server

  • Application

  • Session

For more information, see the IIS documentation.

To retrieve an IIS object, use the getDispatch method of returned Variant and cast this value to the interface to the IIS object (for example IReponse for the Response object).

Note Context properties are not available in MTS 1.0 server processes.

Example


Count, EnumNames, GetProperty Methods Example (Visual J++)

Count, EnumNames, GetProperty Methods Example

import com.ms.com.*;
import com.ms.mtx.*;
import asp.*;

// Get the context object
IGetContextProperties icp;
IResponse iresp;
Variant av = new Variant();
icp = (IGetContextProperties)MTx.GetObjectContext();

// Get the Response object
av = icp.GetProperty("Response");
iresp = (IResponse) av.getDispatch();
av.VariantClear();

// Print number of properties
String str;
int pc;
pc = icp.Count();
str = "<p>Number of properties: " + pc + "</p>";
av.putString(str);
iresp.Write (av);
av.VariantClear();

// Iterate over properties collection and print the
// names of the properties
IEnumNames iEnum = null;
int howmany = 1;
String[] names = new String[1];
int fetched;
iEnum = icp.EnumNames();
for ( int i = 0; i < pc; ++i )
{
   fetched = iEnum.Next( howmany, names);
   str = "<p>" + names[0] + "</p>";
   av.putString(str);
   iresp.Write (av);
   av.VariantClear();
}


IObjectContext Interface (Visual J++)

IObjectContext Interface

The IObjectContext interface provides access to the current object's context.

Remarks

New applications should use the Context class.

The IObjectContext interface is declared in the package com.ms.mtx.

You obtain a reference to the IObjectContext interface by calling the MTx.GetObjectContext method. As with any COM object, you must release an ObjectContext object when you're finished using it.

You can use an object's ObjectContext to:

  • Declare that the object's work is complete.

  • Prevent a transaction from being committed, either temporarily or permanently.

  • Instantiate other MTS objects and include their work within the scope of the current object's transaction.

  • Find out if a caller is in a particular role.

  • Find out if security is enabled.

  • Find out if the object is executing within a transaction.

The IObjectContext interface exposes the following methods.

Method | Description
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.
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.

Note When an object calls DisableCommit, EnableCommit, SetComplete, or SetAbort from within a method, two flags (Done and Consistent) are set in its ObjectContext. (See the following table for an explanation.) These flags aren't evaluated by the MTS run-time environment until the object's currently executing method returns to its caller. This means that an object can call these methods any number of times from within one of its own methods, but the last call before the object returns to its client is the one that will be in effect.

Method | Done | Consistent
SetAbort | TRUE | FALSE
SetComplete | TRUE | TRUE
DisableCommit | FALSE | FALSE
EnableCommit | FALSE | TRUE

The Done flag, which allows an object to be deactivated and its transaction to commit or abort, is only evaluated after the object returns from the call that first entered its context. For example, suppose client A calls into object B. Object B calls SetComplete and then calls into object C (passing it a safe reference for a callback). Object C calls back to object B, and then object B returns to client A. Object B won't be deactivated when it returns to object C; it will be deactivated when it returns to client A.

See Also

Basic Security Methods, Passing Object References, Context Objects, Transactions, Deactivating Objects


IObjectContext.CreateInstance Method (Visual J++)

IObjectContext.CreateInstance Method

Instantiates an MTS object.

New applications should call createInstance.

Provided By

IObjectContext Interface

IUnknown CreateInstance (

_Guid clsid,

_Guid iid,

);

Parameter

clsid

[in] The clsid of the type of object to instantiate.

iid

[in] Any interface that's implemented by the object you want to instantiate.

Return Value

A reference to the IUnknown interface on the newly created object.

Remarks

CreateInstance creates a COM object. However, the object will have context only if its component is registered with MTS.

When you create an object by using CreateInstance, the new object's context is derived from the current object's ObjectContext and the declarative properties of the new object's component. The new object always executes within the same activity as the object that created it. If the current object has a transaction, the transaction attribute of the new object's component determines whether or not the new object will execute within the scope of that transaction.

If the component's transaction attribute is set to either Requires a transaction or Supports transactions, the new object inherits its creator's transaction. If the component's transaction attribute is set to Requires a new transaction, MTS initiates a new transaction for the new object. If the component's transaction attribute is set to Does not support transactions, the new object doesn't execute under any transaction.

CreateInstance always returns the IUnknown interface on the newly instantiated object. You should immediately cast the returned value to the interface through which you want to communicate with the new object. The interface ID you pass in the iid parameter doesn't have to be the same interface as the one to which you cast the returned value, but it must be an interface that's implemented by the object you're instantiating.

MTS always uses standard marshaling. Even if a component exposes the IMarshal interface, its IMarshal methods will never be called by the MTS run-time environment.

You can't create MTS objects as part of an aggregation.

Example

See Also

Creating MTS Objects, Transaction Attributes, MTS Component Requirements


IObjectContext.DisableCommit Method (Visual J++)

IObjectContext.DisableCommit Method

Declares that the object's transactional updates are inconsistent and can't be committed in their present state.

New applications should call disableCommit.

Provided By

IObjectContext

void DisableCommit ( );

Remarks

An object that invokes DisableCommit is stateful.

You can use the DisableCommit method to prevent a transaction from committing prematurely between method calls in a stateful object. When an object invokes DisableCommit, it indicates that its work is inconsistent and that it can't complete its work until it receives further method invocations from the client. It also indicates that it needs to maintain its state to perform that work. This prevents the MTS run-time 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.

For example, suppose you have a General Ledger component that updates a database. A client makes multiple calls to a General Ledger object to post entries to various accounts. There's an integrity constraint that says the debits must equal the credits when the final method invocation returns, or the transaction must abort. The General Ledger object has an initialization method in which the client informs it of the sequence of calls the client is going to make, and the General Ledger object calls DisableCommit. The object maintains its state between calls so that after the final call in the sequence is made the object can make sure the integrity constraint is satisfied before allowing its work to be committed.

Example

See Also

Transactions


IObjectContext.DisableCommit Method Example (Visual J++)

IObjectContext.DisableCommit Method Example

import com.ms.mtx.*;

MTx.GetObjectContext().DisableCommit();


IObjectContext.EnableCommit Method (Visual J++)

IObjectContext.EnableCommit Method

Declares that the current object's work is not necessarily finished, but that its transactional updates are consistent and could be committed in their present form.

New applications should call enableCommit.

Provided By

IObjectContext

void EnableCommit ( );

Remarks

When an object calls EnableCommit, it allows the transaction in which it's 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.

Example

See Also

Transactions


IObjectContext.EnableCommit Method Example (Visual J++)

IObjectContext.EnableCommit Method Example

import com.ms.mtx.*;

MTx.GetObjectContext().EnableCommit();

}


IObjectContext.IsCallerInRole Method (Visual J++)

IObjectContext.IsCallerInRole Method

Indicates whether an object's direct caller is in a specified role (either individually or as part of a group).

New applications should call isCallerInRole.

Provided By

IObjectContext

boolean IsCallerInRole (

String bstrRole

);

Parameters

bstrRole

[in] The name of the role in which you want to determine whether the caller is acting.

Return Values

true

Either the caller is in the specified role, or security is not enabled.

false

The caller is not in the specified role.

Remarks

You use this method to determine whether the direct caller of the currently executing method is associated with a specific role. A role is a symbolic name that represents a user or group of users who have specific access permissions to all components in a given package. Developers define roles when they create a component, and roles are mapped to individual users or groups at deployment time.

IsCallerInRole only applies to the direct caller of the currently executing method. (The direct caller is the process calling into the current server process. It can be either a base client process or a server process.) IsCallerInRole doesn't apply to the process that initiated the call sequence from which the current method was called, or to any other callers in that sequence.

Because IsCallerInRole returns true when the object that invokes it is executing in a client's process, it's a good idea to call IsSecurityEnabled before calling IsCallerInRole. If security isn't enabled, IsCallerInRole won't return an accurate result.

Example

See Also

Programmatic Security, Basic Security Methods, Secured Components


IObjectContext.IsCallerInRole, IObjectContext.IsSecurityEnabled Methods Example (Visual J++)

IObjectContext.IsCallerInRole, IObjectContext.IsSecurityEnabled Methods Example

import com.ms.mtx.*;

IObjectContext objContext = null;

objContext = MTx.GetObjectContext();

// Find out if Security is enabled.
if (objContext.IsSecurityEnabled()) {
   //Then find out if the caller is in the right role.
   if (!objContext.IsCallerInRole("Manager")) {
      // If not, do something appropriate here.
   }
}
else {
   // If security's not enabled, do something
   // appropriate here.
}


IObjectContext.IsInTransaction Method (Visual J++)

IObjectContext.IsInTransaction Method

Indicates whether the current object is executing in a transaction.

New applications should call isInTransaction.

Provided By

IObjectContext

boolean IsInTransaction ( );

Return Values

true

The current object is executing within a transaction.

false

The current object is not executing within a transaction.

Remarks

You can use this method to make sure that an object that requires a transaction never runs without one. For example, if a component that requires a transaction is improperly configured in the MTS Explorer, you can use this method to determine that the object doesn't have a transaction. Then you can return an error to alert the user to the problem, or take whatever action is appropriate.

Example

See Also

Transaction Attributes, Transactions


IObjectContext.IsInTransaction Method Example (Visual J++)

IObjectContext.IsInTransaction Method Example

import com.ms.mtx.*;

// Find out if the object is in a transaction.
if (!MTx.GetObjectContext().IsInTransaction()) {
   // If not, do something appropriate here.
}


IObjectContext.IsSecurityEnabled Method (Visual J++)

IObjectContext.IsSecurityEnabled Method

Indicates whether or not security is enabled for the current object. MTS security is enabled unless the object is running in the client's process.

New applications should call isSecurityEnabled.

Provided By

IObjectContext

boolean IsSecurityEnabled ( );

Return Values

true

Security is enabled for this object.

false

Security is not enabled for this object.

Remarks

MTS security is enabled only if an object is running in a server process. This could be either because the object's component was configured to run in a client's process, or because the component and the client are in the same package. If the object is running in the client's process, there is no security checking and IsSecurityEnabled will always return false.

Example

See Also

Programmatic Security, Basic Security Methods, Secured Components


IObjectContext.SetAbort Method (Visual J++)

IObjectContext.SetAbort Method

Declares that the transaction in which the object is executing must be aborted, and that the object should be deactivated on returning from the currently executing method call.

New applications should call setAbort.

Provided By

IObjectContext

void SetAbort ( );

Remarks

The object is deactivated automatically on return from the method in which it called SetAbort. If the object is the root of an automatic transaction, MTS aborts the transaction. If the object is transactional, but not the root of an automatic transaction, the transaction in which it's participating is doomed to abort. (An object is the root of a transaction if the MTS run-time environment has to initiate a new transaction for it. This is the case when the component that provides the object is configured to require a transaction and the object's creator doesn't have one, or when the component is configured to require a new transaction.)

You can call SetAbort in error handlers to ensure that a transaction aborts when an error occurs. You can also call SetAbort at the beginning of a method to protect your object from committing prematurely in the event of an unexpected return and then call SetComplete just before the method returns, if all goes well.

Example

See Also

Transactions, Context Objects, Deactivating Objects


IObjectContext.SetAbort, IObjectContext.SetComplete Methods Example (Visual J++)

IObjectContext.SetAbort, IObjectContext.SetComplete Methods Example

import com.ms.mtx.*;

boolean success = false;
// Do some work here.
// If the work was successful, call SetComplete
if (success)
   MTx.GetObjectContext().SetComplete();
// Otherwise, call SetAbort.
else
   MTx.GetObjectContext().SetAbort();


IObjectContext.SetComplete Method (Visual J++)

IObjectContext.SetComplete Method

Declares that the current object has completed its work and should be deactivated when the currently executing method returns to the client. For objects that are executing within the scope of a transaction, it also indicates that the object's transactional updates can be committed.

New applications should call setComplete.

Provided By

IObjectContext

void SetComplete ( );

Remarks

The object is deactivated automatically on return from the method in which it called SetComplete. If the object is the root of an automatic transaction, MTS attempts to commit the transaction. However, if any object that was participating in the transaction has called SetAbort, or has called DisableCommit and has not subsequently called EnableCommit or SetComplete, the transaction will be aborted. (An object is the root of a transaction if the MTS run-time environment has to initiate a new transaction for it. This is the case when the component that provides the object is configured to require a transaction and the object's creator doesn't have one, or when the component is configured to require a new transaction.)

If an object doesn't need to maintain its state after it returns from a method call, it should call SetComplete so that it can be automatically deactivated as soon as it returns and its resources can be reclaimed.

Example

See Also

Transactions, Context Objects, Deactivating Objects


MTx.SafeRef Method (Visual J++)

MTx.SafeRef Method

Used by an object to obtain a reference to itself that's safe to pass outside its context.

SafeRef is a static method of the MTx class, which is declared in the package com.ms.mtx.

Note The class MTx has only static methods and has no public constructor. You can't create an instance of this class.

New applications should call getSafeRef.

IUnknown SafeRef (

IUnknown obj

);

Parameter

obj

[in] A reference to an interface on the current object.

Return Value

A reference to the IUnknown interface on the current object that's safe to pass outside the current object's context.

Remarks

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 reference to this to a client or to any other object. Once such a reference is passed outside the object's context, it's no longer a valid reference.

Regardless of the interface ID you pass to SafeRef, it always returns the IUnknown interface on the object that calls it. You should immediately cast the returned value to the interface that you want to pass outside the object.

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

Example

See Also

Passing Object References


MTx.SafeRef Method Example (Visual J++)

MTx.SafeRef Method Example

import com.ms.mtx.*;

IMyInterface safeMyObject = null;
IAnotherObject someOtherObject = null;

// Get a safe reference.
safeMyObject = (IMyInterface) MTx.SafeRef(this);

// Invoke a method on another object, passing the
// safe reference so it can call back.
someOtherObject.CallMeBack(safeMyObject);


MTx.GetObjectContext Method (Visual J++)

MTx.GetObjectContext Method

Obtains a reference to the IObjectContext interface on the ObjectContext that's associated with the current MTS object.

GetObjectContext is a static method of the MTx class, which is declared in the package com.ms.mtx.

Note The MTx class has only static methods and has no public constructor. You can't create an instance of this class.

IObjectContext GetObjectContext ( );

Return Value

A reference to the IObjectContext interface on the current object's context. GetObjectContext will return null if it is called from a constructor or finalizer, or if the object's component hasn't been imported into an MTS package.

Remarks

An object should never attempt to pass its ObjectContext reference to another object. If you pass an ObjectContext reference to another object, it will no longer be a valid reference.

Example

See Also

Building Scalable Components, Context Objects, IObjectContext.CreateInstance Method


MTx.GetObjectContext Method, IObjectContext.CreateInstance Method Example (Visual J++)

MTx.GetObjectContext Method, IObjectContext.CreateInstance Method Example

import com.ms.mtx.*;

IAccount account = null;

// Get the object's ObjectContext and
// use it to instantiate another object.
account = (IAccount) MTx.GetObjectContext().
   CreateInstance(CAccount.clsid, IAccount.iid);


IObjectControl Interface (Visual J++)

IObjectControl Interface

You implement the IObjectControl interface when you want to define context-specific initialization and cleanup procedures for your MTS objects and specify whether or not the objects can be recycled. Implementing the IObjectControl interface is optional.

Remarks

The IObjectControl interface is declared in the package com.ms.mtx.

If you implement the IObjectControl interface in your component, the MTS run-time environment automatically calls the IObjectControl methods on your objects at the appropriate times.

When an object supports the IObjectControl interface, MTS calls its Activate method once for each time the object is activated. The Activate method is called before any of the object's other methods are called. You can use this method to perform any context-specific initialization an object may require.

Note An object's context isn't available during object construction (from the object's class factory), so context-specific initialization can't be performed in an object's constructor.

MTS calls the object's Deactivate method each time the object is deactivated. This can be the result of the object returning from a method in which it calls SetComplete or SetAbort, or due to the root of the object's transaction, causing the transaction to complete. You use the Deactivate method to clean up state that you initialized in the Activate method.

After calling the Deactivate method, the MTS run-time environment calls the CanBePooled method. If this method returns true, the deactivated object is placed in an object pool for reuse. If the CanBePooled method returns false, the object is released in the usual way.

Note On systems that don't support object pooling, the value returned by this method is ignored.

The IObjectControl interface is not accessible to an object's clients or to the object itself. Only the MTS run-time environment can invoke the IObjectControl methods.

The IObjectControl interface exposes the following methods.

Method | Description
Activate | Allows an object to perform context-specific initialization whenever it's activated. This method is called by the MTS run-time environment before any other methods are called on the object.
CanBePooled | Allows an object to notify the MTS run-time environment of whether it can be pooled for reuse. Return true if you want instances of this component to be pooled, or false if not.
Deactivate | Allows an object to perform whatever cleanup is necessary before it's recycled or destroyed. This method is called by the MTS run-time environment whenever an object is deactivated.

See Also

Deactivating Objects, Object Pooling and Recycling


IObjectControl.Activate Method (Visual J++)

IObjectControl.Activate Method

Implementing this method allows an MTS object to perform context-specific initialization whenever it's activated. This method is called by the MTS run-time environment before any other methods are called on the object.

Provided By

IObjectContext

void Activate ( );

Remarks

Whenever a client calls an MTS object that isn't already activated, the MTS run-time environment automatically activates the object. This is called just-in-time activation. For components that support the IObjectControl interface, MTS invokes the object's Activate method before passing the client's method call on to the object. This allows objects to do context-specific initialization.

Because an object's context isn't available during object construction, you can't perform any initialization that involves the ObjectContextinside the constructor. Instead, you should implement the Activate method and place all your context-specific initialization procedures there.

For example, you can use the Activate method to obtain a reference to an object's context and store it in a member variable. Then the context reference is available to any method that requires it, and you don't have to acquire a new one and then release it every time you use it. Once you have a reference to the object's context, you can use the IObjectContext methods to check whether security is enabled, whether the object is executing in a transaction, or whether the caller is in a particular role.

If you're enabling object recycling (returning true from the CanBePooled method), the Activate method must be able to handle both newly created and recycled objects. When the Activate method returns, there should be no distinguishable difference between a new object and a recycled one.

Example

See Also

Deactivating Objects, Object Pooling and Recycling


IObjectControl.Activate Method Example (Visual J++)

IObjectControl.Activate Method Example

import com.ms.mtx.*;

void Activate()
{
   // Get a reference to the object's context here,
   // so it can be used by any method that may be
   // called during this activation of the object.
   m_ObjectContext = MTx.GetObjectContext()
}


IObjectControl.CanBePooled Method (Visual J++)

IObjectControl.CanBePooled Method

Implementing this method allows an MTS object to notify the MTS run-time environment of whether it can be pooled for reuse. Return true if you want the object to be pooled for reuse, or false if not.

Provided By

IObjectContext

boolean CanBePooled ( );

Return Values

true

Notifies the MTS run-time environment that it can pool this object on deactivation for later reuse.

false

Notifies the MTS run-time environment that it should not pool this object on deactivation, but should release its last reference to the object so that the object will be destroyed.

Remarks

When an object returns true from the CanBePooledmethod, it indicates to the MTS run-time environment that it can be added to an object pool after deactivation rather than being destroyed. Whenever an instance is required, one is drawn from the pool rather than created by the class factory.

The way recycling works is that an object cleans itself up in its Deactivate method and is returned to an object pool. Later, when an instance of the same component is needed, the cleaned up object can be reused. For this to work, an object must be accessible on different threads each time it's activated. Recycling isn't possible under the apartment threading model because, in that model, although an object can be instantiated on any thread, it can only be used by the thread on which it was instantiated. If you want instances of a component to be recyclable, you should register the component with the ThreadingModel Registry value set to Both. This indicates to MTS that the component's objects can be called from different threads.

In MTS, these objects will run under the apartment threading model and won't be recycled even if they return true from the CanBePooled method. However, if you configure a component to support both threading models, the component will run under the current version of MTS and will also be able to take advantage of recycling as soon as it becomes available, without any changes to the code.

Deciding whether to enable recycling is a matter of weighing the costs and benefits. Recycling requires writing additional code, and there's a risk that some state may be inadvertently retained from one activation to the next. When you allow objects to be pooled, you have to be very careful in your Activate and Deactivate methods to ensure that a recycled object is always restored to a state that's equivalent to the state of a newly created object. Another consideration to take into account is the amount of resources required to maintain an object pool. Objects that hold a lot of resources can be expensive to pool. However, in certain situations, recycling can be extremely efficient, resulting in improved performance and increased scalability. The trade-off is between the cost of holding onto resources while objects are pooled (and inactive) versus the cost of creating and destroying the resources.

It's usually best to enable recycling for objects that cost more to create than they cost to reinitialize. For example, if a component contains a complex structure, and that structure can be reused, it could save a lot of time if the structure didn't have to be recreated every time an instance of the component was activated. This is a case in which you might want to enable recycling, which you would do by returning true from the CanBePooled method.

You could still create the structure in the object's constructor because the constructor is only called once, when a new object is created by the class factory. When recycling is enabled, that only happens when the object pool is empty. The Activate method, on the other hand, is called whether a new instance is created by the class factory or a recycled instance is drawn from the pool. Similarly, the Deactivate method is called every time the object is deactivated, whether it's being destroyed or returned to the pool for recycling.

So, in this example, you'd use the object's Activate method to initialize, or reinitialize, the structure that's being reused, and you'd use the Deactivate method to restore the object to a state that the Activate method can handle. (The Activate method must be able to handle both new objects created by the class factory and reused objects drawn from the pool.) This combined use of the Activate, Deactivate, and CanBePooled methods eliminates the need to recreate reusable resources every time an instance is activated.

For some objects, recycling isn't efficient. For example, if an object acquires a lot of state during its lifetime that isn't reusable, and has little to do during its construction, it's usually cheaper to create a new instance whenever one is needed. In that case, you would return false from the CanBePooled method.

Note Returning true from the CanBePooled method doesn't guarantee that objects will be recycled; it only gives the MTS run-time environment permission to recycle them. On systems that don't support object pooling, a return value of true is ignored. Returning false from the CanBePooled method guarantees that instances of a component aren't recycled.

Example

See Also

Deactivating Objects, Object Pooling and Recycling


IObjectControl.CanBePooled Method Example (Visual J++)

IObjectControl.CanBePooled Method Example

import com.ms.mtx.*;

boolean CanBePooled()
{
   // This object should not be recycled,
   // so return false.
   return false;
}


IObjectControl.Deactivate Method (Visual J++)

IObjectControl.Deactivate Method

Implementing this method allows an MTS object to perform any cleanup required before it's recycled or destroyed. This method is called by the MTS run-time environment whenever an object is deactivated.

Provided By

IObjectContext

void Deactivate ( );.

Remarks

The MTS run-time environment calls the Deactivate method whenever an object that supports the IObjectControl interface is deactivated. An object is deactivated when it returns from a method in which it called SetComplete or SetAbort, when the transaction in which it executed is committed or aborted, or when the last client to hold a reference on it releases its reference.

If the component supports recycling (returns true from the CanBePooled method), you should use the Deactivate method to reset the object's state to the state in which the Activate method expects to find it. You can also use the Deactivate method to release the object's ObjectContext reference or to do other context-specific cleanup. Even if an object supports recycling, it can be beneficial to release certain reusable resources in its Deactivate method. For example, ODBC provides its own connection pooling. It's more efficient to pool a database connection in a general connection pool where it can be used by other objects than it is to keep it tied to a specific object in an object pool.

Example

See Also

Deactivating Objects, Object Pooling and Recycling


IObjectControl.Deactivate Method Example (Visual J++)

IObjectControl.Deactivate Method Example

import com.ms.mtx.*;

void Deactivate( )
{
   // Perform any necessary cleanup here.
}


ISharedPropertyGroupManager Interface (Visual J++)

ISharedPropertyGroupManager Interface

The ISharedPropertyGroupManager interface is used to create shared property groups and to obtain access to existing shared property groups.

Remarks

The ISharedPropertyGroupManager interface is declared in the package com.ms.mtx.

You can access the ISharedPropertyGroupManager interface by creating an instance of the SharedPropertyGroupManager by using either IObjectContext.CreateInstance or new SharedPropertyGroupManager. It makes no difference which you use.

CreateInstance method of the ObjectContext object. It makes no difference which you use.

The Shared Property Manager is a resource dispenser that you can use to share state among multiple objects within a server process. You can't use global variables in a distributed environment because of concurrency and name collision issues. The Shared Property Manager eliminates name collisions by providing shared property groups, which establish unique name spaces for the shared properties they contain. The Shared Property Manager also implements locks and semaphores to protect shared properties from simultaneous access, which could result in lost updates and could leave the properties in an unpredictable state.

Shared properties can be shared only by objects running in the same process. If you want instances of different components to share properties, you have to 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.

It's also important for components sharing properties to 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.

You should always instantiate the SharedPropertyGroupManager, SharedPropertyGroup, and SharedProperty objects from MTS objects rather than from a base client. If a base client creates shared property groups and properties, the shared properties are inside the base client's process, not in a server process. This means MTS objects can't share the properties unless the objects, too, are running in the client's process (which is generally not a good idea).

Note When you set the isolation mode to LOCKMODE_METHOD, the Shared Property Manager requires access to the calling object's ObjectContext. You can't use this isolation mode to create a shared property group from within an object's constructor or from a non-MTS object because ObjectContext isn't available during object construction and a non-MTS object doesn't have an ObjectContext.

The ISharedPropertyGroupManager interface exposes the following methods and properties.

Method | Description
CreatePropertyGroup | Creates a new SharedPropertyGroup with a string name as an identifier. If a group with the specified name already exists, CreatePropertyGroup returns a reference to the existing group.
getGroup | Returns a reference to an existing shared property group, given a string name by which it can be identified.
get_NewEnum | Returns a reference to an enumerator that iterates through a list of all the shared property groups in a given process.

See Also

Sharing State


ISharedPropertyGroupManager.CreatePropertyGroup Method (Visual J++)

ISharedPropertyGroupManager.CreatePropertyGroup Method

Creates and returns a reference to a new shared property group. If a property group with the specified name already exists, CreatePropertyGroup returns a reference to the existing group.

Provided By

ISharedPropertyGroupManager Interface

ISharedPropertyGroup CreatePropertyGroup (

String name,

int[] lockmode,

int[] releasemode,

boolean[] exists,

);

Parameters

name

[in] The name of the shared property group to create.

lockmode

[in, out] An array of one integer that specifies the isolation mode for the properties in the new shared property group. See the table that lists lockmode constants later in this topic. If the value of the exists parameter is set to true on return from this method, the lockmode value you passed in is ignored and the value returned in this parameter is the isolation mode that was assigned when the property group was created.

releasemode

[in, out] An array of one integer that specifies the release mode for the properties in the new shared property group. See the table that lists releasemode constants later in this topic. If the value of the exists parameter is set to true on return from this method, the releasemode value you passed in is ignored and the value returned in this parameter is the release mode that was assigned when the property group was created.

exists

[out] An array of one boolean that's set to true on return from this method if the shared property group specified in the name parameter existed prior to this call, and false if the property group was created by this call.

Settings

The following constants are used in the lockmode parameter to specify the effective isolation mode for a shared property group. These constants are static final members of the ISharedPropertyGroupManager interface.

Constant | Value | Description
LOCKMODE _SETGET | 0 | Default. Locks a property during a getValue or putValuecall, 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 it doesn't prevent other clients from concurrently accessing other properties in the same group.
LOCKMODE _METHOD | 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.

Note When you set the isolation mode to LOCKMODE_METHOD, the Shared Property Manager requires access to the calling object's ObjectContext. You can't use this isolation mode to create a shared property group from within an object's constructor or from a non-MTS object because ObjectContext isn't available during object construction and a base client doesn't have an ObjectContext.

The following constants are used in the releasemode parameter to specify the effective release mode for a shared property group. These constants are static final members of the ISharedPropertyGroupManager interface.

Constant | Value | Description
RELEASE MODE_ STANDARD | 0 | When all clients have released their references on the property group, the property group is automatically destroyed. (This is the default COM mode.)
RELEASE MODE_ PROCESS | 1 | The property group isn't destroyed until the process in which it was created has terminated.

Return Value

A reference to a shared property group identified by the string expression passed in the name parameter, or null if an error is encountered.

Remarks

The CreatePropertyGroup method sets the value in exists to true if the property group it returns existed prior to the current call. This occurs when another object in the same process has already called CreatePropertyGroup with the same property group name. The CreatePropertyGroup method sets the value in exists to false if the returned property group was created by the current call.

The isolation mode and release mode are assigned when the property group is originally created and aren't changed if a subsequent call passes different values in these parameters. The caller should always check the value of exists on return from this method. If exists is set to true, the caller should check the values returned in lockmode and releasemode to determine the isolation and release modes in effect for the property group. For example:

propGp = propGpMgr.CreatePropertyGroup
   ("Counter", aiIsolationMode,
   aiReleaseMode, afAlreadyExists);
if (afAlreadyExists[0]) {
   if ((aiIsolationMode[0] !=
      ISharedPropertyGroupManager.LOCKMODE_METHOD) ||
      (aiReleaseMode[0] ISharedPropertyGroupManager.
      RELEASEMODE_PROCESS)) {
      // Do something appropriate.
   }
}
If*

Note An object should never attempt to pass a shared property group reference to another object. If the reference is passed outside of the object that acquired it, it's no longer a valid reference.

Example

See Also

Sharing State, IObjectContext Interface, ISharedPropertyGroup Interface


ISharedPropertyGroupManager.getGroup Method (Visual J++)

ISharedPropertyGroupManager.getGroup Method

Returns a reference to an existing shared property group.

Provided By

ISharedPropertyGroupManager Interface

ISharedPropertyGroup getGroup (

String name,

);

Parameters

name

[in] The name of the shared property group to retrieve.

Return Value

A reference to the shared property group specified in the name parameter, or null if the property group doesn't exist.

Example

See Also

Sharing State, ISharedPropertyGroupManager Interface


ISharedPropertyGroupManager.get_NewEnum Method (Visual J++)

ISharedPropertyGroupManager.get_NewEnum Method

Returns a reference to an enumerator that you can use to iterate through all the shared property groups in a process.

Provided By

ISharedPropertyGroupManager Interface

IUnknown get_NewEnum ( );

Return Value

A reference to the IUnknown interface on a new enumerator object that you can use to iterate through the list of all the shared property groups in the process.

Remarks

You use the get_NewEnum method to obtain a reference to an enumerator object. You should immediately cast the returned value to the IEnumVariant interface. This interface exposes several methods you can use to iterate through a list of string expressions representing shared property group names. Once you have a name, you can use the getGroup method to obtain a reference to the shared property group it represents. When you enumerate the shared property groups, all groups will be included. However, if you then call CreatePropertyGroup to add a new group, the existing enumerator won't include the new group even if you call Reset or Clone. To include the new group, you must create a new enumerator by calling NewEnum again.

Example

See Also

ISharedPropertyGroupManager.getGroup Method, ISharedPropertyGroup Interface


ISharedPropertyGroupManager.get_NewEnum Method Example (Visual J++)

get_NewEnum Method Example

import com.ms.mtx.*;

ISharedPropertyGroupManager spgm = null;
IEnumVariant myEnum = null;
Variant v;
int i;

// Get the enumerator object and
// cast the returned interface to IEnumVariant.
myEnum = (IEnumVariant)spgm.get_NewEnum();

// Use the enumerator to iterate through
// the property group names.
for(i = 0; i < 10; i++){
   v = Enum.Next(1);
   // Do something with the returned
   // property group names.
}


ISharedPropertyGroup Interface (Visual J++)

ISharedPropertyGroup Interface

The ISharedPropertyGroup interface is used to create and access the shared properties in a shared property group.

Remarks

The ISharedPropertyGroup interface is declared in the package com.ms.mtx.

You can access the ISharedPropertyGroup interface by creating a SharedPropertyGroup object with the ISharedPropertyGroupManager.CreatePropertyGroup method.

The ISharedPropertyGroup interface exposes the following methods.

CreateProperty | Creates a new shared property identified by a string expression that's unique within its property group.
CreatePropertyByPosition | Creates a new shared property identified by a numeric index within its property group.
getProperty | Returns a reference to a shared property, given the string name by which the property is identified.
getPropertyByPosition | Returns a reference to a shared property, given its numeric index in the shared property group.

See Also

Sharing State, ISharedPropertyGroupManager Interface


ISharedPropertyGroup.CreateProperty Method (Visual J++)

ISharedPropertyGroup.CreateProperty Method

Creates and returns a reference to a new SharedProperty with a specified name. If a shared property by that name already exists, CreateProperty returns a reference to the existing property.

Provided By

ISharedPropertyGroup Interface

ISharedProperty CreateProperty (

String name,

boolean[] exists;

);

Parameters

name

[in] The name of the property to create. You can use this name later to obtain a reference to this property by using the getProperty method.

exists

[out] An array of one Boolean value that's set to true on return from this method if the shared property specified in the name parameter existed prior to this call, and false if the property was created by this call.

Return Value

A reference to a shared property object with the name specified in the name parameter, or null if an error is encountered.

Remarks

When you create a shared property, its value is set to the default, which is a Variant with an integer value of 0.

If you create a shared property with the CreateProperty method, you can access that property only by using the getProperty method. You can't assign a numeric index to the same property and then access it by using the getPropertyByPosition method.

The same shared property group can contain some shared property objects that are identified by name and others that are identified by position.

Example

See Also

Sharing State, ISharedPropertyGroup::CreatePropertyByPosition Method, ISharedPropertyGroup::get_PropertyByPosition Method, ISharedPropertyGroup::get_Property Method


ISharedPropertyGroup.CreatePropertyByPosition Method (Visual J++)

ISharedPropertyGroup.CreatePropertyByPosition Method

Creates a new shared property identified by a numeric index that's unique within the property group. If a shared property with the specified index already exists, CreatePropertyByPosition returns a reference to the existing one.

Provided By

ISharedPropertyGroup Interface

ISharedProperty CreatePropertyByPosition (

int index,

boolean[] exists;

);

Parameters

index

[in] The numeric index within the SharedPropertyGroup by which the new property will be referenced. You can use this index later to retrieve the shared property with the getPropertyByPosition method.

exists

[out] An array of one boolean value. If exists is set to true on return from this method, the shared property specified by index existed prior to this call. If it's set to false, the property was created by this call.

Return Value

A reference to a shared property object that's identified by the numeric index passed in the index parameter, or null if an error is encountered.

Remarks

When you create a shared property, its value is set to the default, which is a Variant with an integer value of 0.

If you create a SharedProperty object with the CreatePropertyByPosition method, you can access that property only by using the getPropertyByPosition method. You can't assign a string name to the same property and then access it by using the getProperty method. Accessing a property by position is faster than accessing a property by using a string name because it requires less overhead.

The same shared property group can contain some SharedProperty objects that are identified by position and others that are identified by name.

Example

See Also

Sharing State, ISharedPropertyGroup::CreateProperty Method, ISharedPropertyGroup::get_PropertyByPosition Method, ISharedPropertyGroup::get_Property Method


ISharedPropertyGroup.CreatePropertyByPosition Method Example (Visual J++)

ISharedPropertyGroup.CreatePropertyByPosition Method Example

import com.ms.mtx.*;

ISharedPropertyGroupManager propGpMgr = null;
ISharedPropertyGroup propGp = null;
ISharedProperty propNextNum = null;
Variant vNext;
int iNextValue = 0;
boolean[] afAlreadyExists = new boolean[1];
int[] aiIsolationMode = new int[1];
aiIsolationMode[0] =
   ISharedPropertyGroupManager.LOCKMODE_SETGET;
int[] aiReleaseMode = new int[1];
aiReleaseMode[0] =
   ISharedPropertyGroupManager.RELEASEMODE_PROCESS;

// Create the SharedPropertyGroupManager,
// SharedPropertyGroup, and SharedProperty.
propGpMgr = new SharedPropertyGroupManager();
propGp = propGpMgr.CreatePropertyGroup
   ("Counter", aiIsolationMode,
   aiReleaseMode, afAlreadyExists);
propNextNum = propGp.CreatePropertyByPosition
   (0, fAlreadyExists);

// Get the next number and increment it.
VariantInit(&vNext);
vNext = propNextNum.getValue();
iNextValue = vNext.getInt();
vNext.putInt(iNextValue + 1);
propNextNum.putValue(vNext);


ISharedPropertyGroup.getProperty Method (Visual J++)

ISharedPropertyGroup.getProperty Method

Returns a reference to an existing shared property identified by a string name.

Provided By

ISharedPropertyGroup Interface

ISharedProperty getProperty (

String name,

);

Parameters

name

[in] A string expression that contains the name of the shared property to retrieve.

Return Value

A reference to the shared property specified in the name parameter, or null if the property doesn't exist.

Remarks

You can use only the getProperty method to access properties that were created with the CreateProperty method. To access properties that were created with the CreatePropertyByPosition method, use the getPropertyByPosition method.

Example

See Also

Sharing State, ISharedPropertyGroup::CreateProperty Method, ISharedPropertyGroup::CreatePropertyByPosition Method, ISharedPropertyGroup::get_PropertyByPosition Method


ISharedPropertyGroupManager.getGroup, ISharedPropertyGroup.getProperty Methods Example (Visual J++)

ISharedPropertyGroupManager.getGroup, ISharedPropertyGroup.getProperty Methods Example

import com.ms.mtx.*;

ISharedPropertyGroupManager propGpMgr = null;
ISharedPropertyGroup propGp = null;
ISharedProperty propNextNum = null;
Variant vNext;
int iNextValue = 0;

// Get the SharedPropertyGroupManager,
// SharedPropertyGroup, and SharedProperty.
propGpMgr = new SharedPropertyGroupManager();
propGp = propGpMgr.getGroup("Counter");
propNextNum = propGp.getProperty("NextNum");

// Get the next number and increment it.
VariantInit(&vNext);
vNext = propNextNum.getValue();
iNextValue = vNext.getInt();
vNext.putInt(iNextValue + 1);
propNextNum.putValue(vNext);


ISharedPropertyGroup.getPropertyByPosition Method (Visual J++)

ISharedPropertyGroup.getPropertyByPosition Method

Returns a reference to an existing shared property identified by its numeric index within the property group.

Provided By

ISharedPropertyGroup Interface

ISharedProperty getPropertyByPosition (

INT index,

);

Parameters

index

[in] The numeric index within the SharedPropertyGroup of the property to retrieve.

Return Value

A reference to the shared property specified by the index parameter, or null if the property doesn't exist.

Remarks

You can use only the getPropertyByPosition method to access properties that were created with the CreatePropertyByPosition method. To access properties that were created with the CreateProperty method, use the getProperty method.

Example

See Also

Sharing State, ISharedPropertyGroup::CreateProperty Method, ISharedPropertyGroup::CreatePropertyByPosition Method, ISharedPropertyGroup::get_Property Method


ISharedPropertyGroup.getPropertyByPosition Method Example (Visual J++)

ISharedPropertyGroup.getPropertyByPosition Method Example

import com.ms.mtx.*;

ISharedPropertyGroupManager propGpMgr = null;
ISharedPropertyGroup propGp = null;
ISharedProperty propNextNum = null;
Variant vNext;
int iNextValue = 0;

// Get the SharedPropertyGroupManager,
// SharedPropertyGroup, and SharedProperty.
propGpMgr = new SharedPropertyGroupManager();
propGp = propGpMgr.getGroup("Counter");
propNextNum = propGp.getPropertyByPosition(0);

// Get the next number and increment it.
VariantInit(&vNext);
vNext = propNextNum.getValue();
iNextValue = vNext.getInt();
vNext.putInt(iNextValue + 1);
propNextNum.putValue(vNext);


ISharedProperty Interface (Visual J++)

ISharedProperty Interface

The ISharedProperty interface is used to set or retrieve the value of a shared property. A shared property can contain any data type that can be represented by a variant.

Remarks

The ISharedProperty interface is declared in the package com.ms.mtx.

You can access the ISharedProperty interface by creating a SharedProperty object with the ISharedPropertyGroup.CreateProperty method or the ISharedPropertyGroup.CreatePropertyByPosition method.

A SharedProperty object can be created or accessed only from within a SharedPropertyGroup.

The ISharedProperty interface exposes the following methods.

Method | Description
getValue | Retrieves the value of a shared property.
putValue | Sets the value of a shared property.

See Also

Sharing State, MTS Supported Variant Types


ISharedProperty.getValue Method (Visual J++)

ISharedProperty.getValue Method

Retrieves the value of a shared property.

Provided By

ISharedProperty Interface

Variant getValue ( );

Return Value

A Variant in which the value of the shared property will be returned.

Example

See Also

MTS Supported Variant Types, Sharing State


ISharedProperty.putValue Method (Visual J++)

ISharedProperty.putValue Method

Assigns a value to a shared property.

Provided By

ISharedProperty Interface

void putValue (

Variant value

);

Parameters

value

[in] A Variant containing the value to assign to the SharedProperty object.

Example

See Also

MTS Supported Variant Types, Sharing State


CreatePropertyGroup, CreateProperty, putValue, getValue Methods Example (Visual J++)

CreatePropertyGroup, CreateProperty, putValue, getValue Methods Example

import com.ms.mtx.*;

ISharedPropertyGroupManager propGpMgr = null;
ISharedPropertyGroup propGp = null;
ISharedProperty propNextNum = null;
Variant vNext;
int iNextValue = 0;
boolean[] afAlreadyExists = new boolean[1];
int[] aiIsolationMode = new int[1];
aiIsolationMode[0] =
   ISharedPropertyGroupManager.LOCKMODE_SETGET;
int[] aiReleaseMode = new int[1];
aiReleaseMode[0] =
   ISharedPropertyGroupManager.RELEASEMODE_PROCESS;

// Create the SharedPropertyGroupManager,
// SharedPropertyGroup, and SharedProperty.
propGpMgr = new SharedPropertyGroupManager();
propGp = propGpMgr.CreatePropertyGroup
   ("Counter", aiIsolationMode,
   aiReleaseMode, afAlreadyExists);
propNextNum = propGp.CreateProperty
   ("NextNum", afAlreadyExists);

// Get the next number and increment it.
VariantInit(&vNext);
vNext = propNextNum.getValue();
iNextValue = vNext.getInt();
vNext.putInt(iNextValue + 1);
propNextNum.putValue(vNext);


ITransactionContextEx Interface (Visual J++)

ITransactionContextEx Interface

The ITransactionContextEx interface is used by a base client to compose the work of one or more MTS objects into an atomic transaction and to commit or abort the transaction.

Remarks

The ITransactionContextEx interface is declared in the package com.ms.mtx.

You can use a TransactionContextEx object to scope a transaction from a base client. You begin the transaction by instantiating a TransactionContextEx object, and you end the transaction by calling Commit or Abort on the object. The base client itself never executes within the transaction.

The TransactionContextEx component is a standard MTS component. The component's transaction attribute is set to Requires a new transaction, which means that a TransactionContextEx object is always the root of a transaction. When a base client instantiates an object by using the ITransactionContextEx.CreateInstance method, the new object and its descendants will participate in theTransactionContextEx object's transaction unless the new object's transaction attribute is set to Requires a new transaction or Does not support transactions.

You could easily write your own TransactionContextEx component. You would simply create a component that implements the methods Commit, Abort, and CreateInstance, and set the component's transaction attribute to Requires a new transaction. The three methods would do nothing more than call GetObjectContext and invoke their ObjectContext object's SetComplete, SetAbort, and CreateInstance methods, respectively.

Before you use TransactionContextEx to compose the work of existing components in a transaction, you should consider implementing a separate component that not only composes their work but encapsulates it into a reusable unit. This new component would not only serve the needs of the current base client, but other clients could also use it. In one approach, the base client instantiates a TransactionContextEx object, calls its CreateInstance method to instantiate other objects, calls various methods on those objects, and finally calls Commit or Abort on the TransactionContextEx object. In the other approach, you create a new component that requires a transaction. This new component instantiates the other objects using its ObjectContext object's CreateInstance method, calls the relevant methods on those other objects itself, and then calls SetComplete or SetAbort on its ObjectContext when it's done. Using this approach, the base client only needs to instantiate this one object, and invoke one method on it, and the object does the rest of the work. When other clients require the same functionality, they can reuse the new component.

You obtain a reference to the ITransactionContextEx interface by creating a TransactionContextEx object. For example:

new TransactionContextEx();

The ITransactionContextEx interface exposes the following methods.

Method | Description
Abort | Aborts the work of all MTS objects participating in the current transaction. The transaction is completed on return from this method.
Commit | Attempts to commit the work of all MTS objects participating in the current transaction. If any of the MTS objects participating in the transaction have called SetAbort or DisableCommit, or if a system error has occurred, the transaction will be aborted. Otherwise, the transaction will be committed. In either case, the transaction is completed on return from this method.
CreateInstance | Instantiates another MTS object. If the component that provides the object is configured to support or require a transaction, then the new object runs under the transaction of the TransactionContextExobject.

See Also

Transaction Context Objects, Base Clients, Transactions


ITransactionContextEx.Abort Method (Visual J++)

ITransactionContextEx.Abort Method

Aborts the current transaction.

Provided By

ITransactionContextEx Interface

void Abort ( );

Remarks

When a base client calls Abort, all objects that participated in the transaction are automatically deactivated. Any database updates made by those objects are rolled back. The transaction is completed on return from this method. If another call is made on the TransactionContextEx object after the TransactionContextEx object has returned from a call in which it called the Abort method, a new transaction is started.

Example

See Also

Transaction Context Objects, Base Clients, Transactions, SetAbort


ITransactionContext.Abort, ITransactionContext.Commit Methods Example (Visual J++)

ITransactionContext.Abort, ITransactionContext.Commit Methods Example

import com.ms.mtx.*;

ITransactionContextEx myTransactionContext = null;
IMyObject myObject = null;
boolean userCanceled = false;

// Get TransactionContextEx.
myTransactionContext = new TransactionContextEx();

// Create an instance of MyObject.
myObject = myTransactionContext.CreateInstance (CMyObject.clsid, IMyObject.iid);

// Do some work here.

// If something goes wrong, abort the transaction.
if (userCanceled)
   myTransactionContext.Abort();

// Otherwise, commit it.
else
   pTransactionContext.Commit();


ITransactionContextEx.Commit Method (Visual J++)

ITransactionContextEx.Commit Method

Attempts to commit the current transaction.

Provided By

ITransactionContextEx Interface

void Commit ( );

Remarks

Calling Commit doesn't guarantee that a transaction will be committed. If any MTS object that was part of the transaction has returned from a method after calling SetAbort, the transaction will be aborted. If any object that was part of the transaction has called DisableCommit and hasn't yet called EnableCommit or SetComplete, the transaction will also be aborted. Any error that causes Microsoft Distributed Transaction Coordinator to abort a transaction will also abort an MTS transaction.

When a base client calls Commit, regardless of whether the transaction commits or aborts, the transaction is completed on return from this method and all objects that participated in the transaction are automatically deactivated. If another call comes in after the TransactionContextEx object has returned from a call in which it called the Commit method, a new transaction is started.

Example

See Also

Transaction Context Objects, Base Clients, Transactions, SetComplete


ITransactionContextEx.CreateInstance Method (Visual J++)

ITransactionContextEx.CreateInstance Method

Instantiates an MTS object that will execute within the scope of the transaction that was initiated with the creation of the TransactionContextEx object.

Provided By

ITransactionContextEx Interface

IUnknown CreateInstance (

_Guid clsid,

_Guid iid,

);

Parameter

clsid

[in] A reference to the CLSID of the type of object to instantiate.

iid

[in] Any interface that's implemented by the object you want to instantiate.

Return Value

A reference to the IUnknown interface on a new instance of the MTS component specified in the clsid parameter.

Remarks

When a base client uses the ITransactionContextEx.CreateInstance method to instantiate an MTS object, the new object executes within the transaction context object's activity. If the transaction attribute of the new object's component is set to either Supports transactions or Requires a transaction, the new object also inherits the transaction initiated with the creation of the TransactionContextEx object. However, if the component that provides the new object has its transaction attribute set to Does not support transactions, the object neither inherits the transaction nor passes it on to objects it subsequently creates. If the component that provides the new object has its transaction attribute set to Requires a new transaction, the MTS run-time environment initiates a new transaction for the new object, and that transaction is the one that's inherited by objects it subsequently creates.

CreateInstance always returns the IUnknown interface on the newly instantiated object. You should immediately cast the returned value to the interface with which you want to communicate with the new object. The interface ID you pass in the iid parameter doesn't have to be the same interface to which you cast the returned value, but it must be an interface that's implemented by the object you want to instantiate.

MTS always uses standard marshaling. Even if a component exposes the IMarshal interface, its IMarshal methods will never be called by the MTS run-time environment.

Note You can't create MTS objects as part of an aggregation.

Example

See Also

Transaction Context Objects, Base Clients, Transactions, CreateInstance


ITransactionContextEx.CreateInstance Method Example (Visual J++)

ITransactionContext.CreateInstance Method Example

import com.ms.mtx.*;

ITransactionContextEx myTransactionContext = null;
IMyObject myObject = null;

// Get TransactionContextEx.
myTransactionContext = new TransactionContextEx();

// Create an instance of MyObject.
myObject = (IMyObject)
   myTransactionContext.CreateInstance
   (CMyObject.clsid, IMyObject.iid);


Building MTS Applications

This topic contains information that a component application developer needs to understand before building Microsoft® Transaction Server (MTS) applications.

MTS Component Requirements

Business Logic in MTS Components

Packaging MTS Components

Calling MTS Components

Holding State in Objects

Database Access Interfaces with MTS

Developing MTS Components with Java

Debugging MTS Components

Automating MTS Deployment

MTS Error Diagnosis


Transaction Server Component Requirements

MTS Component Requirements

An MTS component is a type of COM component that executes in the MTS run-time environment. In addition to the COM requirements, MTS requires that the component must be a dynamic-link library (DLL). Components that are implemented as executable files (.exe files) cannot execute in the MTS run-time environment. For example, if you build a Remote Automation server executable file with Microsoft® Visual Basic®, you must rebuild it as a DLL.

Note MTS 1.0 had a limit of 100 methods per component. In MTS 2.0, this limit is now 1024 methods.

Components and TreatAs and AutoTreatAs Registry Keys

COM allows one server to emulate another using the TreatAs or AutoTreatAs registry keys. Suppose a client creates a component of a given CLSID using CoCreateInstance. If the TreatAs or AutoTreatAs key is present under that CLSID in the registry, COM will instead create the component indicated by this key. This effectively routes client requests to the emulating server. (The same routing is possible using the ProgID if the client refers to its servers by ProgID instead of CLSID.)

Components being emulated using TreatAs or AutoTreatAs are not valid MTS components and cannot be imported. If the emulation is to be added after the component is imported, the component must be deleted from the MTS catalog, then replaced with the emulating component.

Additional Requirements for Visual C++ Components

  • The component must have a standard class factory.

  • The component DLL must implement and export the standard DllGetClassObject function and support the IClassFactory interface. MTS uses this interface to create objects. IClassFactory::CreateInstance must return a unique instance of an MTS object.

  • The component must only export interfaces that use standard marshaling. For more information, see Passing Parameters.

  • All component interfaces and coclasses must be described by a type library. The information in the type library is used by the MTS Explorer to extract information about the installed components.

  • For custom interfaces that cannot be marshaled using standard Automation support, you must build the proxy-stub DLL with MIDL version 3.00.44 or later (provided with in the Microsoft Platform SDK for Windows NT version 4.0); use the –Oicf and /MD compiler options; and link the DLL with the mtxih.lib, LE32.lib, and ADVAPI32.lib libraries provided by MTS. The mtxih.lib library must be the first file that you link into your proxy-stub DLL. If the component has both a type library and a proxy-stub DLL, MTS will use the proxy-stub DLL.

  • The component must export the DllRegisterServer function and perform self-registration of its CLSID, ProgID, interfaces, and type library in this routine.

Development tools such as Visual Basic and the Active Template Library, which is available with Microsoft Visual C++, allow you to generate interfaces that COM can marshal automatically. These interfaces, known as dual interfaces, are derived from IDispatch and use the built-in Automation marshaling support.

MFC Extension DLLs

MTS components should not be built as MFC Extension DLLs because such DLLs can be loaded only by MFC applications. A COM component, and therefore an MTS component, should be built so that it can be loaded into any process, regardless of the type of application that started the process.

For more information on MFC Extension DLLs, see the Microsoft Visual C++ Programmer’s Guide.

Registering MTS Components

You manage MTS components by using the MTS Explorer. Before a component can run with context in the MTS run-time environment, you must use the MTS Explorer to define the component in the MTS catalog. In addition to keeping track of a component's basic COM attributes, such as the name of the implementation DLL, the MTS catalog maintains a set of MTS–specific attributes. MTS uses these attributes to provide capabilities in addition to those provided by COM. For example, the transaction attribute controls the transactional characteristics of a component.

The MTS Explorer assigns components to a package that controls the assignment of components to server processes and control client access to components.

Note MTS allows only a single server process associated with a given package to run on a computer at a time. MTS writes a warning event message to the log if you attempt to start a second instance of an already active package. However, COM does not explicitly disallow multiple servers running the same COM classes. MTS writes a warning message to the log in the event that two threads try to start the package at the same time. This event is especially likely on a symmetric multiprocessing (SMP) computer where the two package invocations are concurrent. In these cases, MTS enforces a rule of one server process for each package by terminating one of the extra packages. COM then connects to the one server process still running and successfully returns.

Registration for Components Developed with Visual Basic

Whenever you recompile a COM DLL (or “ActiveX DLL”) project in Visual Basic, Visual Basic rewrites all of the registry entries for all of the components (Visual Basic classes) that live in that DLL. Additionally, Visual Basic may generate new GUIDs (depending on your project configuration) to identify the components in that DLL. This means that your MTS components are no longer properly registered in the MTS catalog.

There are two solutions to this problem. First, there is a Refresh button in the Transaction Server Explorer, as well as the Refresh All Components command on the Tools menu. If you use this command Microsoft Transaction Server will repair all inconsistencies in registry entries of components currently in the right pane of the Explorer.

If you install the development version of Microsoft Transaction Server, check the VB Addin option to enable a feature that will automatically refresh your components after recompiling them. The next time you run Visual Basic, the add-in will be automatically installed in your Visual Basic IDE. You can also turn the feature on and off on a per-project basis by selecting or deselecting the MTxServer RegRefresh | AutoRefresh after compile command on the Visual Basic Add-Ins menu. If you decide you want to refresh all of your Microsoft Transaction Server components at any given time, you can use the MTxServer RegRefresh | Refresh all components now command on the Add-Ins menu.

Important The Visual Basic Add-in works with Visual Basic versions 4.0, 5.0 and 6.0. Once installed, it will automatically refresh the Transaction Server catalog with the changes made during each compile. The add-in menu option to enable and disable the automatic refresh is no longer supported.

Using the add-in will properly refresh the MTS catalog, even after Visual Basic compilations that generate new component GUIDs. Note also that the Binary Compatibility setting in the Visual Basic Project Properties dialog box can be used to stop Visual Basic from generating new component GUIDs.

Refreshing the MTS catalog depends on you not changing the ProgIDs of your components. In Visual Basic, a component’s ProgID is formed by the following concatenation: project name.class name. If you change either of these items, you will have to reinstall your component(s) in the MTS Explorer.

Building your component in Visual Basic without selecting theBinary Compatibility option replaces your old CLSID and IID on each compile. This has disadvantages when developing an MTS component, even with the Visual Basic add-in enabled.

  • Roles you assigned to the interface using the MTS Explorer are lost, since the interface IID is obsolete.

  • Proxies and registry configurations you distributed to remote machines no longer refer to your component and must all be updated.

  • Packages you exported which contain your component require re-exporting since the package definition file GUIDs are now out of sync.

Running COM Components Under MTS

Exercise caution when registering a standard COM component (one developed without regard to MTS) to execute under MTS control.

First, ensure that references are safely passed between contexts (see Passing Object References).

Second, if the component uses other components, consider running them under MTS. Rewrite the code for creating objects in these components to use CreateInstance (see Creating MTS Objects).

Third, you can effectively use automatic transactions only with components that indicate the completion of their work by calling either the SetComplete or SetAbort methods. If a component does not use these methods, an automatic transaction can only be completed when the client releases the object. MTS will attempt to commit the transaction, but there is no way for the client to determine whether the transaction has been committed or aborted. Therefore, it is recommended that you do not register components as Requires a transaction or Requires a new transaction unless they use SetComplete and SetAbort.

Including Multiple Components in DLLs

You can implement multiple components in the same DLL. The MTS Explorer allows components from the same DLL to be installed in separate packages.

Including Type Libraries and Proxy-Stub DLLs in MTS Components

Development tools supporting ActiveX components can merge your type library or proxy-stub DLL with your implementation DLL. If you do not want to distribute your implementation DLL to client computers, keep your type libraries and proxy-stub DLLs separate from your implementation DLLs. The client only needs a type library or custom proxy-stub DLL to use your server application remotely.


Business Logic in MTS Components

This topic describes how to enact business logic in MTS components.

Granularity is determined by the number of tasks performed by a component. The granularity of a component affects the performance, debugging, and reusability of your MTS components. A fine-grained component performs a single task, such as calculating tax on a sales order. Fine-grained components consume and release resources quickly after completing a task. A component that enacts a single business rule can facilitate testing packages, because isolating individual tasks in components makes testing your applications easier. In addition, fine-grained components are easily reused in other packages. In the following example, a component performs a single task: adding a customer record to the database.

Function Update(ByVal strEmail As String, _
ByVal bNewCust As Boolean, ByVal strContact As String,_
ByVal strPhoneNumber As String, _
ByVal strNightPhoneNumber As String)

    Dim ctxObject As ObjectContext
    Set ctxObject = GetObjectContext

    On Error GoTo ErrorHandler

' Code accesses the customer row from the database.
' Customer information is updated with information
' that was passed in.
'
    ctxObject.SetComplete

    Exit Function

This simple component uses system resources efficiently (passing parameters by value), is easy to debug (single function), and also reusable in any other application that maintains customer data.

A coarse-grained component performs multiple tasks. Coarse-grained components are generally harder to debug and reuse in applications. For example, a PlaceOrder component might add a new order, update inventory, and update customer information. PlaceOrder is a more coarsely grained component because it performs more "work" by adding, updating, and deleting customer, inventory and order information.

For more information about components' shared resources, see Holding State in Objects.


Packaging MTS Components

This document describes how you should package your MTS components. Consider the following design issues when defining package boundaries:

  • Activation

  • Shared resources

  • Fault isolation

  • Security isolation

Activation

You can select either of the following Activation levels for your packages:

  • Library (running within the same process as the client that creates the object)

  • Server (on the same computer but in a different process)

MTS provides a way to set up remote components by using the Remote Computer and Remote Component folders in the MTS Explorer hierarchy. For more information about "pulling" or "pushing" components between computers, see the Administrator's Guide.

By default, components run in a server process on the local computer. If you run your components within the MTS server process, you enable resource sharing, security, and easier administration by using the MTS Explorer for your component. Running components in-process provides an immediate performance benefit, because you do not have to marshal parameters cross-process. However, in-process components do not support declarative security and you lose fault isolation.

Sidebar: In-process Components and Security

Note that in-process components do not support declarative security or offer the benefits of process isolation. In-process components will run in any process that creates the component. Role checking is disabled between in-process components because IsCallerInRole returns True. In other words, the direct caller always passes the authorization check.

Also, it is recommended that you place your components as close as possible to the data source. If you are building a distributed application with a number of packages running on local and remote servers, try to group your components according to the location of your data. For example, in the following figure, the Accounting server hosts an Accounting package and Accounting database.

Shared Resources

Sharing resources in a multiuser environment results in faster applications that scale more easily. Note that only components marked with the Local activation setting can share resources. Package your components to take advantage of the resource sharing and pooling that MTS provides for your application.

Pool your resources by server process.Note that MTS runs each hosted package in a separate server process. The fewer pools you have running on your server, the more efficiently you pool resources, so try to group components that share "expensive" resources, such as connections to a specific database. If you reuse the expensive resources within your package, you can greatly improve the performance and scaling of your application. For example, if you have a database lookup and a database update component running in a customer maintenance application, package those components together so that they can share database connections.

Fault Isolation

Fault isolation requires separating components into packages that can operate in their own server process. Components in the same package share the same server process if all the activation settings are the same. By placing components in separate packages, you can mitigate the impact of a component failure because each package runs in a separate server process.

You can also use fault isolation to test new components. You can stage updates to MTS applications by introducing new components. Fault isolation for packages greatly reduces the risk of your local server package failing when you introduce a new component to a shared environment.

Security Isolation

MTS security roles represent a logical group of user accounts which are mapped to Microsoft Windows NT® domain users and groups during the deployment of the package. You can use the MTS Explorer to define declarative authorization checking by applying roles to components and component interfaces. Applying a security role to a component defines access privileges for any user assigned as a member of that security role. Users not assigned to a role with access privileges to a package cannot use the package. Because security authorization occurs between packages rather than between components within a package, it is recommended that you consider the MTS security model when determining your package boundaries. Note that security isolation only applies to packages with components running under the Server activation setting.

Security authorization is checked when a method call crosses a package boundary, such as when a client calls into a package or one package calls another. When you package your components, make sure you group components that can safely call each other without requiring security checks within one package.

All components within a package run under the identity established for the package. If you run under different identities, separate them into two different packages.

You can use declarative security between the client and server, and database security based on package identity between the server and data source. You can restrict access to a data source by assigning an identity to a package, and configuring the database to accept updates according to package identity.

If you use package identity to set up your database security, the database recognizes the package identity as a single user. If database access occurs under an identity set by the package, the database connection set up for the package identity name can be used by all the users mapped to role or roles for that package. This kind of resource sharing improves application performance and scalability.


Calling MTS Components

This topic covers the following:

  • Calling MTS Components using DCOM

  • Calling MTS Components from an Active Server Page

  • Calling MTS Components from a Web Browser-Resident Component

For more information on the methods for creating MTS objects, see Creating MTS Objects.

Calling MTS Components from a Client Application

MTS components can be located on a separate computer from the client. A client can call a remote MTS component using DCOM, HTTP, or Remote Automation. To run an MTS component on the client computer, the client computer must have MTS installed.

Calling MTS Components through DCOM

DCOM is the standard transport for calling MTS components. To enable DCOM calls to MTS components, you must configure the following:

Client Registry Settings — The easiest way to configure your client application to call a remote MTS component is to use the application executable utility, which automatically configures client registry settings. For more information, see the Administrator's Guide.

DCOM Security Settings — You may have to configure the Impersonation Level and Authentication Level on both client and server computers. MTS works properly using the default values for these settings: Identify for Impersonation Level and Connect for Authentication Level. Make the necessary changes in the MTS Explorer at the package level. Changing default settings by using the DCOM configuration utility (dcomcnfg.exe) is not recommended.

Windows 98 includes DCOM support. However, if you want to use Microsoft Windows 95 clients with MTS, install DCOM for Windows® 95. For the latest information on DCOM support for Windows 95, see http://www.microsoft.com/oledev on the World Wide Web.

Calling MTS Components through Remote Automation

Remote Automation was introduced with Visual Basic version 4.0, before the introduction of DCOM. It is useful for 16-bit clients, because DCOM works only in 32-bit environments. To use Remote Automation with MTS, the Remote Automation Manager (RACMAN) must be running on the server where the MTS components are installed. For more information, see the Visual Basic documentation.

Note You cannot use MTS security Remote Automation since all calls are made using the RACMAN identity. Because RACMAN does not impersonate when calling the components on the server, the client identity cannot be determined.

Calling MTS Components through HTTP

There are two ways a client can call an MTS component through HTTP:

  • Call an Active Server Page (ASP), which in turn calls the MTS component using DCOM.

  • Call the MTS component from a Web browser – resident component using the ActiveX Data Objects (ADO) Remote Data Service (RDS), which in turn uses HTTP. For more information about RDS, see http://www.microsoft.com/adc.

Calling MTS Components from an Active Server Page

You can call MTS components from Active Server Pages (ASPs). You can create an MTS object from an ASP by calling Server.CreateObject. Note that if the MTS component has implemented the OnStartPage and OnEndPage methods, the OnStartPage method is called at this time.

You can run your MTS components in-process with or out-of-process with Internet Information Server (IIS). If you run your MTS components in-process with IIS, be aware that if MTS encounters an unexpected internal error condition or an unhandled application error such as a general-protection fault inside a component method call, it immediately results in a failfast, thus terminating the process and IIS.

By default, IIS 3.0 disables calling out-of-process components. To enable calling out-of-process components, modify the following registry entry

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\ASP\Parameters

by setting the AllowOutOfProcCmpnts key to 1.

Calling MTS Components from a Web Browser-Resident Component

You can call an MTS component from a Web browser – resident component. Use the application executable utility to configure that client, and then use the HTML <OBJECT> tag to call that component. You can also use the <OBJECT> tag to create an MTS object in-process with the browser client. Remember that MTS must be installed on the client computer for an MTS component to run in-process.

The component should be made safe for scripting, either through a component category entry in the registry, or by supporting the IObjectSafety interface.

Remote Data Service (RDS) also allows you to create web browser – resident components using the <OBJECT> tag. RDS supports the following:

  • HTTP

  • HTTPS (HTTP over Secure Socket Layer)

  • DCOM

  • In-process server

Except for in-process objects, the CreateObject method of the DataSpace object creates a proxy for the MTS object that runs in a local or remote server process.

You must configure the following registry key to the Prog ID of the object that you want to call:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\ADCLaunch


Holding State in Objects

Although there are many benefits to using stateless MTS objects, there are cases where holding state is desirable. This topic provides some guidelines in deciding where state is held in your application.

The following diagram shows a three-tier architecture:

Typically, the latency between tiers differs greatly. Calls between the presentation tier and business tier are often an order of magnitude slower than calls between the business tier and data tier. As a result, held state is more costly when calling into the business tier.

However, it often makes sense to hold state within the transaction boundary itself. For example, the objects in the data tier may represent a complex join across many tables in separate databases. Reconstructing the data object state is potentially more inefficient than the cost of the resources held by those objects while they remain active.

Since objects lose state on transaction boundaries, if you need to hold state across transactions, use the Shared Property Manager or store the state in a database.

Example: Order-Entry Application

There are two separate issues when considering the effects of holding state in an application:

  • Network roundtrips — More frequent network roundtrips and slower connections extend the lifetime of the called MTS object.

  • Held resources — Holding state often means holding onto a resource, such as a database connection, and potentially, locks on the database.

Consider the example of an online shopping application. The client chooses items from a catalog and submits an order. Order processing is handled by a business object, which in turn stores the order in a database (not shown).

One way of building the application is for the client to call an Order object, with each call adding or removing an item from the order:

This application has the following properties:

  • Client maintains no state.

  • Server maintains state across multiple calls.

  • Many network roundtrips.

  • High contention for resources. The database connection is held for the lifetime of the Order object. This is not a very scalable solution.

You can require that the client cache the items in an array or recordset:

This application has the following properties:

  • Stateful client.

  • Server is virtually stateless with one call to the server.

  • Fewer network roundtrips.

  • Less contention for resources. This is a scalable solution.

Concurrency

In addition to network bandwidth and resources, concurrency affects application performance. There are two types of concurrency:

  • Pessimistic — As soon as editing begins, the database locks the records being changed. The records are unlocked when all changes are complete. No two users can access the same record at the same time.

  • Optimistic — The database locks the records being changed only when the changes are committed. Two users can access the same record at the same time, and the database must be able to reconcile, or simply reject, changed records that have been edited by multiple users prior to commit.

Implementing a server cache implies optimistic concurrency. The server does not have to hold locks on the database, thus freeing resources.

However, if there is high contention for the resource, pessimistic concurrency may be preferred. It is easier to reject a request to access a database and have the server try again than it is to reconcile cached, out-of-date data with a rapidly changing database.


Database Access Interfaces with MTS

This topic describes the database access interface options for MTS applications. You can use the Open Database Connectivity (ODBC) Application Programming Interface (API) to access a resource manager (which is a system service that manages durable data), or a data access model that functions over the ODBC layer. Because the ODBC version 3.0 Driver Manager is an MTS resource dispenser, data accessed via ODBC is automatically protected by your object's transaction. For object transactions, an ODBC-compliant database must support the following:

  • The database's ODBC driver must be thread safe. It also must be able to connect to the driver from one thread, use the connection from another thread, and disconnect from another thread.

  • If ODBC is used from within a transactional component, then the ODBC driver must also support the SQL_ATTR_ENLIST_IN_DTC connection attribute. This is how the ODBC Driver Manager asks the ODBC driver to enlist a connection on a transaction. You can make your component transactional by setting the transaction property for your component in the MTS Explorer. If you are using a database without a resource dispenser that can recognize MTS transactions, contact your database vendor to obtain the required support.

The following table summarizes database requirements for full MTS support.

Requirements | Description | Resources (if applicable)
Support for the OLE transactions specification, or support for XA protocol | Enables direct interaction with Distributed Transaction Coordinator (DTC). Use the XA Mapper to interact with DTC | MTS SDK in the Microsoft Platform SDK.
ODBC driver | Platform requirement for MTS server components | ODBC version 3.0 SDK
ODBC driver support for the ODBC version 3.0 SetConnectAttr SQL_ATTR_ENLIST_ IN_DTC call. | MTS uses this call to pass the transaction identifier to the ODBC driver. The ODBC driver then passes the transaction identifier to the database engine. | ODBC version 3.0 SDK
Fully thread-safe ODBC driver | ODBC driver must be able to handle concurrent calls from any thread at any time. | ODBC version 3.0 SDK
ODBC driver must not require thread affinity | ODBC driver must be able to connect to the driver from one thread, use the connection from another thread, and disconnect from another thread. | ODBC version 3.0 SDK

If a memory access violation in the mtx.exe process occurs within the driver after 60 seconds of inactivity, you may be using an ODBC driver that is not thread safe or requires thread affinity. The fault occurs in the driver when the inactive connections are being disconnected.

MTS Distributed Transaction Coordinator

MTS uses the services of the Microsoft Distributed Transaction Coordinator (DTC) for transaction coordination. DTC is a system service that coordinates transactions that span multiple resource managers. Work can be committed as a single transaction, even if it spans multiple resource managers, potentially on separate computers. DTC was initially released as part of Microsoft SQL Server version 6.5, and is included as part of MTS. DTC implements a two-phase commit protocol that ensures that the transaction outcome (either commit or abort) is consistent across all resource managers involved in a transaction. DTC supports resource managers that implement OLE Transactions, X/Open XA protocols, and LU 6.2 Sync Level 2.

Choosing your Data Access Model

The following table summarizes commonly used data access models supported by MTS.

Interface | Description
Microsoft ActiveX Data Objects (ADO), Remote Data Service (RDS) | ADO offers one common yet extensible programming model for accessing data. ADO includes the ability to pass query results (Recordsets) between server and client, and the ability to pass updated Recordsets from client to server using RDS.
OLE DB | OLE DB is a low-level interface that provides uniform access to any tabular data source. You cannot call OLE DB interfaces directly from Microsoft® Visual Basic® because OLE DB is a pointer-based interface. A Visual Basic client can access an OLE DB data source through ADO.
Open DataBase Connectivity (ODBC) | ODBC is a recognized standard interface to relational data sources. ODBC is fast and provides a universal interface that is not optimized for any specific data source.
Remote Data Objects (RDO) | RDO is a thin object layer interface to the ODBC API. It is specifically designed to access remote ODBC relational data sources. Note that adding the rdExecDirect flag to the .Execute method ensures that a temporaray stored procedure is not created when using two DSN’s from the same transaction. See the RDO documentation for more details.

The diagram below illustrates how MTS components interact with the different data access interfaces:

ADO is not specifically designed for relational or ISAM databases, but as an object interface to any data source. ADO can access relational databases, ISAM, text, hierarchical, or any type of data source, as long as a data access provider exists. ADO is built around a set of core functions that all data sources are expected to implement. ADO can access native OLE DB data sources, including a specific OLE DB provider that provides access to ODBC drivers. ADO ships with the OLE DB Software Development Kit (SDK).

RDO does have some functionality that is not currently implemented in ADO, including the following:

  • Events on the Engine, Connection, Resultset, and Column objects

  • Asynchronous operations

  • Queries as methods

  • Enhanced batch-mode error and contingency handling

  • Tight integration with Visual Basic, as in the Query Connection designer and TSQL debugger.

Future versions of ADO will provide a superset of RDO version 2.0 functionality and provide a far more sophisticated interface, in addition to an easier programming model. Because ADO is an Automation-based component, any application or language capable of working with Automation objects can use it.


Developing MTS Components with Java

You can develop Java MTS components using tools provided by MTS and Visual J++. It is also recommended that you install the latest version of the Microsoft SDK for Java, available at http://www.microsoft.com/java.

This section contains the following topics:

  • Using Visual J++ 6.0

  • Using the Microsoft SDK for Java

  • Using an MTS Component from Java

  • Using the Java Sample Bank Components

Using Visual J++ 6.0

If you are using Microsoft Visual J++, not only can you create MTS components completely within the integrated development environment (IDE), but the steps required to build a component for MTS are greatly simplified over earlier versions of Visual J++.

Because Visual J++ 6.0 has many new features to support building and debugging components for MTS, some of the steps in the following sections do not apply to Visual J++ 6.0. For more information about building an MTS component in Visual J++ 6.0, see the Visual J++ 6.0 documentation.

Using the Microsoft SDK for Java

Note The following steps assume you are using the Microsoft SDK for Java along with Visual J++ 1.0 or 1.1. (If you have Visual J++ 6.0, these steps are not necessary. For more information, see the Visual J++ 6.0 documentation.)

To implement a component in Java, follow these steps

  1. Run the ActiveX Wizard for Java (available with Visual J++) for each Java class file to create an IDL file. Follow the instructions when the wizard asks you to modify your class declaration.

  2. Modify the IDL files to add JAVACLASS and PROGID to the coclass attributes. For more information, see Using IDL Files with Java Components.

  3. Run the ActiveX Component Wizard for Java again. Use the IDL files that you created in Step 1 to create type libraries for your components.

  4. This will create a set of Java class files, typically under \%systemroot%\Java\Trustlib. It will create one class file for each custom interface, and one class file for each coclass in the library.

  5. Run JAVAGUID against each class file generated in Step 3. For more information, see Working with GUIDs in Java.

  6. Recompile your Java implementation classes.

  7. Run EXEGEN to convert the type libraries and class files into a DLL. For more information, see Using EXEGEN to Create DLLs.

  8. Use the MTS Explorer to install the DLL.

Using IDL Files with Java Components

To specify the custom attributes in a type library, add the following in your IDL or ODL file:

#include <JavaAttr.h>

Within the attributes section of a coclass, specify the JAVACLASS:

JAVACLASS("package.class")

You may optionally specify a PROGID:

PROGID("Progid")

For example:

   [
      uuid(a2cda060-2d38-11d0-b94b-0080c7394688),
      helpstring("Account Class"),
      JAVACLASS("Account.AccountObj"),
      PROGID("Bank.Account.VJ"),
      TRANSACTION_REQUIRED
   ]
   coclass CAccount
   {
      [default] interface IAccount;
   };

Using EXEGEN to Create DLLs

EXEGEN is the Java executable file generator. To use this file, copy it to the appropriate destination folders (usually \JavaSDK\bin). This version of EXEGEN.EXE is capable of creating DLL files from Java classes, and can also include user-specified resources in its output files. This version of EXEGEN no longer supports the /base: directive. Class files are always included with the proper name. It supports a new /D directive that causes it to generate a DLL file instead of an EXE.

EXEGEN is now capable of reading five types of input files:

  • Java class files

  • RES files containg resources to be included

  • Executable files containg resources to be included

  • TLB files containing type libraries to be included

  • Text files describing which classes should be registered (DLL only).

If you use EXEGEN to create a DLL, the DLL can self-register any included Java classes that implement COM objects. There are two ways to tell EXEGEN which classes should be registered:

  • Include a type library that contains custom attributes for the classes. This is the preferred method.

    – or –

  • Include a text file as input that gives EXEGEN the necessary directions. Each line of the text file describes one Java class, using the following keywords:
    • class:JavaClassName

      Required keyword.

    • clsid:{.....}

      Optional keyword that specifies the clsid GUID. If omitted, EXEGEN chooses a unique GUID.

    • progid:ProgId

      Optional keyword. If omitted, the class will be registered without a progid.

Working with GUIDs in Java

When an MTS method uses a GUID parameter, you must pass an instance of class com.ms.com._Guid. Do not use class Guid, CLSID or IID from package com.ms.com; they will not work and they are deprecated. The definition of class _Guid is:

package com.ms.com;
public final class _Guid {

   // Constructors
   public _Guid (String s);
   public _Guid (byte[] b);
   public _Guid (int a, short b, short c,
      byte b0, byte b1, byte b2, byte b3,
      byte b4, byte b5, byte b6, byte b7);
   public _Guid ();

   // methods
   public void set(byte[] b);
   public void set(String s);
   public void set(int a, short b, short c,
      byte b0, byte b1, byte b2, byte b3,
      byte b4, byte b5, byte b6, byte b7);

   public byte[] toByteArray();
   public String toString();
}

Instances of this class can be constructed from a String (in the form “{00000000-0000-0000-0000-000000000000}”), from an array of 16 bytes, or from the usual parts of a Guid. Once constructed, the value can also be changed. Method toByteArray will return an array of 16 bytes as stored in the Guid, and method toString will return a string in the same form used by the constructor.

JAVAGUID.EXE

Microsoft Transaction Server supplies a tool, JAVAGUID.EXE, that will post-process the output of JAVATLB. If you are using Visual J++ 6.0 or later, the use of this tool is not required.

JAVAGUID performs the following for each class file:

  • If any method takes a GUID as a parameter, the class of that parameter will be changed to com.ms.com._Guid.

  • If the class file is an interface derived from a type library, a public static final member named iid will be added to the class. This member will contain the interface ID of the interface.

  • If the class file represents a coclass derived from a type library, a public static final member named clsid will be added to the class. This member will contain the CLSID of the class.

JAVAGUID can only be executed from the command line. It takes one or more parameters which are names of class files to update.

The functionality in JAVAGUID has been included with the JACTIVEX tool from Visual J++ 6.0. Additionally, JACTIVEX automatically adds clsid and iid members to COM wrapper classes.

The clsid and iid members are useful as parameters to IObjectContext.CreateInstance and ITransactionContextEx.CreateInstance.

Using an MTS Component from Java

To use an MTS component from Java, run the Java Type Library Wizard against the type library for the component. This will create several Java class files, typically under \%systemroot%\Java\TrustLib. It will create one class file for each custom interface, and one class file for each coclass in the library.

Assume, for example, that the type library contained one interface named IMyInterface, and one coclass, named CMyClass.

From Java, you can create a new instance of the component by executing

new CMyClass()

If you want to control transaction boundaries in the class, you can execute

ITransactionContextEx.CreateInstance ( CMyClass.clsid, IMyInterface.iid )

You should never call Java’s new operator on the class that you implemented. Instead, use one of the following techniques:

  • Use Java's new operator on the class created by the Java Type Library Wizard. This will cause the Java VM to call CoCreateInstance.

  • Call MTx.GetObjectContext().CreateInstance (clsid, iid);
    This will create a new instance in the same activity as the current instance. This only works if the calling code is itself an MTS component.

  • If you have a reference to an ITransactionContextEx object, call its CreateInstance method. This will create a new instance in the transaction owned by the ITransactionContextEx object.

All of these techinques will result in the creation of a new instance of the class that you implemented.

Using the Java Sample Bank Components

The Java Sample Bank components are automatically configured by MTS Setup and require no additional steps in order to run them.

If you want to recompile the Java Sample Bank components, follow these steps:

  1. Run the SetJavaDev.bat file located in the \mts\Samples\Account.VJ folder. Javatlb.exe must be in your path for this batch file to run properly.

  2. Recompile your Java component implementation classes.

  3. After you recompile the component classes, use the mkdll.bat file located in the \mts\Samples\Account.VJ folder to generate and register vjacct.dll. Exegen.exe must be in your path for this batch file to run properly. You can also add running mkdll.bat as a build step to your Visual J++ project to simplify recompiling.

  4. Using the MTS Explorer, import the new components into the Sample Bank package.


Debugging MTS Components

This document describes techniques for debugging MTS components written in Microsoft® Visual Basic®, Microsoft Visual C++®, and Microsoft Visual J++™. These techniques are just suggestions for successfully debugging MTS components; you can choose your debugging environment and techniques according to your application needs.

If you are using MTS components in a distributed environment, it is recommended that you debug your components on a single computer before deploying to multiple servers. Components that function without error in a package on a local computer usually run successfully over a distributed network. If you do encounter problems with distributed components, you must test and debug both the client and server machines to determine the problem. It is also recommended that you stress test your application with as many clients as possible. You can build a test client that simulates multiple clients to perform the stress test on your application.

To help you debug your components, you should set the MTS transaction timeout to a higher number than the default 60 seconds; otherwise, during debugging, MTS aborts the transaction after this time has lapsed. All subsequent calls to the component return immediately with CONTEXT_E_ABORTED. Any changes to the transaction timeout value made during debugging may not take effect until you restart your debugging environment.

For debugging MTS components written in a specific language, see the following topics:

Debugging Visual Basic MTS Components

Debugging Visual C++ MTS Components

Debugging Java Classes


Debugging Visual Basic MTS Components

Using Visual Basic 6.0

Microsoft Transaction Server components written in Microsoft Visual Basic version 6.0 can be debugged directly in the Visual Basic Integrated Development Environment (IDE).

The following steps describing how to debug a Visual Basic COM component do not apply to Visual Basic version 6.0 or later.

For more information about debugging MTS components using Visual Basic version 6.0, see the Visual Basic version 6.0 documentation.

Using Visual Basic 5.0

Microsoft Transaction Server components written in Microsoft Visual Basic version 5.0 or Visual C++ version 5.0 can be debugged in the Microsoft Visual Studio 97 Integrated Development Environment (IDE).

If you want to debug your components after they are compiled, you cannot use the Visual Basic 5.0 debugger, which only debugs at design time. To debug a compiled Visual Basic component, you will need to use the functionality of the Visual Studio 97 debugger.

Follow these steps to configure Visual Studio to debug MTS components built with Visual Basic 5.0:

  1. In Visual Basic, click Properties on the Project menu and then click the Compile tab to select the Compile to Native Code and the Create Symbolic Debug Info checkbox. It is also recommended that you select the No Optimization checkbox while debugging.

  2. In the MTS Explorer, right-click the package in which your component is installed, and select the Properties option. Place your cursor over the Package ID, and select and copy the GUID to the clipboard.

  3. Open Visual Studio. On the File menu, click Open and select the DLL containing the component that you want to debug.

  4. Select Project Settings, and then click the Debug tab. Select the MTS executable for the debug session (\mtx\mtx.exe). Enter the program arguments as /p:{<package GUID>} for the package GUID that you copied from the package properties. MTS 2.0 allows for the package name to be used in place of the GUID. Open the .cls files containing the code that you want to debug and then set your breakpoints. If you also want to display variable information in the debug environment, go to the Visual Studio Tools menu, select Options, and then select the Debug tab. In the Debug tab, place a check next to Display Unicode strings.

  5. In the MTS Explorer, shut down all server processes.

  6. In Visual Studio, select Build, then select Start Debug. Then select Go to run the server process that will host your component(s), and set breakpoints to step through your code.

  7. Run your client application to access and debug your components in Visual Studio.

  8. Before you deploy your application, remember to select one of the optimizing options in the Compile tab on the Project menu of Visual Basic (set to No Optimization in Step 1), clear the Create Symbolic Debug Info checkbox, and recompile the project.

To facilitate application debugging using Visual Basic 5.0, a component that uses ObjectContext can be debugged by enabling a special version of the object context. This debug-only version is enabled by creating the registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Transaction Server\Debug\RunWithoutContext

Note that when running in debug mode, none of the functionality of MTS is enabled. GetObjectContext will return the debug ObjectContext rather than returning Nothing.

When running in this debug mode, the ObjectContext operates as follows:

  • ObjectContext.CreateInstance - calls COM CoCreateInstance (no context flows, no transactions, and so on)

  • ObjectContext.SetComplete - no effect

  • ObjectContext.SetAbort - no effect

  • ObjectContext.EnableCommit - no effect

  • ObjectContext.DisableCommit - no effect

  • ObjectContext.IsInTransaction - returns FALSE

  • ObjectContext.IsSecurityEnabled - returns FALSE

  • ObjectContext.IsCallerInRole - returns TRUE (same as normal when IsSecurityEnabled is FALSE)

You can also develop your own testing message box functions to generate an assert in an MTS Visual Basic component. The following sample code can to used to display error messages while debugging Visual Basic code. You can also use this in conjunction with the Microsoft Windows NT® debugger (WinDbg.exe), which is a 32-bit application that, along with a collection of DLLs, is used for debugging the Kernel, device drivers, and applications. Note that you must enter DEBUGGING = -1 in the Conditional Compilation dialog box (located on the Make tab of the Project Properties dialog box) to enable the assert.

The following code provides an example.

#If DEBUGGING Then
    'API Functions
    Private Declare Sub OutputDebugStringA _
        Lib "KERNEL32" (ByVal strError As String)
    Private Declare Function MessageBoxA _
        Lib "USER32" (ByVal hwnd As Long, _
        ByVal lpText As String, _
        ByVal lpCaption As String, _
        ByVal uType As Long) As Long
    'API Constants
    Private Const API_NULL                  As Long = 0
    Private Const MB_ICONERROR              As Long = &H10
    Private Const MB_SERVICE_NOTIFICATION As Long = &H200000

Public Sub DebugPrint(ByVal strError As String)
    Call OutputDebugStringA(strError)
End Sub

Public Sub DebugMessage(ByVal strError As String)
   Dim lngReturn As Long
        lngReturn = MessageBoxA(API_NULL, strError, "Error In Component", _
            MB_ICONERROR Or MB_SERVICE_NOTIFICATION)
End Sub
#End If

You can then run checks through your code to aid stress debugging, such as in the following code:

SetobjObjectContext=GetObjectContext()
#If DEBUGGING Then
If objObjectContext Is Nothing Then Call DebugMessage("Context is Not Available")
#End If


Debugging Visual C++ MTS Components

You can use the Visual C++ Integrated Development Environment (IDE) to debug MTS components written in Visual C++, including components that call SQL Server functions or stored procedures. For more information, see Debugging Visual Basic MTS Components.

The following information applies to components that have their activation property set to In a dedicated server process.

Microsoft Transaction Server supports the COM transparent remote debugging infrastructure. If transparent remote debugging is enabled, then stepping into a client process will automatically stop at the actual object's code in the server process, even if the server is on a different computer on the network. A debugging session is automatically started on the server process if necessary. Similarly, single stepping past the return address of code in a server object will automatically stop just past the corresponding call site in the client's process.

In Microsoft Visual C++, selecting the OLE RPC debugging check box (on the Tools menu, select the Options submenu and choose the Debug property sheet) enables transparent remote debugging. It is not known at this time whether other debuggers support this infrastructure.

You can also debug your Microsoft Transaction Server component DLL in Visual C++ by performing the following steps. Each of these steps is made either inside the MTS Explorer or inside of a Visual C++ session with your MTS DLL project.

  1. Shutdown server processes using the MTS Explorer. To do this, right-click My Computer, and select Shutdown Server Process.

  2. In your Visual C++ session, under Project, Settings, Debug, General, set the program arguments to the following string: "/p: PackageName", for example:

  3. /p:"Sample Bank"

  4. In the same property sheet, set the executable to the full path of the Mtx.exe process, for example: "c:\MTx\MTx.exe".

  5. Set breakpoints in your component DLL, and you are ready to debug.

  6. Run the server process (in the Build menu, select Start Debug and click Go.)

The following information applies to in-process component DLLs that have their activation property set to In the creator's process.

You can debug your in-process MTS component DLL in Visual C++ by performing the following steps. Each of these steps is made inside a Visual C++ session with your base process project.

  1. Set the component DLL under Build, Settings, Debug, Additional DLLs.

  2. Now you are ready to step into or set breakpoints in your component DLL at will.

If you are using Visual Studio and Microsoft Foundation Classes (MFC) to debug, the TRACE macro can facilitate your debugging. The TRACE macro is an output debug function that traces debugging output to evaluate argument validity. The TRACE macro expressions specify a variable number of arguments that are used in exactly the same way that a variable number of arguments are used in the run-time function printf. The TRACE macro provides similar functionality to the printf function by sending a formatted string to a dump device such as a file or debug monitor. Like printf for C programs under Microsoft® MS-DOS®, the TRACE macro is a convenient way to track the value of variables as your program executes. In the Debug environment, the TRACE macro output goes to afxDump. In the Release environment, the TRACE macro output does nothing.

Example:

// example for TRACE
int i = 1;
char sz[] = "one";
TRACE( "Integer = %d, String = %s\n", i, sz );
// Output: 'Integer = 1, String = one'

The TRACE macro is available only in the debug version of MFC, but a similar function could be written for use without MFC. For more information on using the TRACE macro, see the "MFC Debugging Support" section in Microsoft® Visual C++® Programmer's Guide.

Note that you should avoid using standard ASSERT code in Visual C++. Instead, it is recommended that you write assert macros like a MessageBox using the MB_SERVICE_NOTIFICATION flag, and TRACE macro statements using the OutputDebugString function call.


Debugging Java Classes

Using Visual J++ 6.0

Because you can use Visual J++ version 6.0 to debug MTS components completely within the Visual J++ Integrated Development Environment (IDE), the debugging topics below do not apply to Visual J++ 6.0, and are included only for those using earlier versions of Visual J++.

For more information about debugging MTS components in Visual J++ 6.0, please see the Visual J++ 6.0 documentation.

Using Visual J++ 1.1 or 1.0

If you are using Visual J++ 1.1 or earlier, you cannot use the Visual J++ IDE to debug your MTS component. Once your Java class is converted into an MTS component it is not possible to step through the code in the Visual J++ debugger.

Using Visual J++ to Debug Java Classes

Microsoft Visual J++ provides a Java debugger that you can use to set breakpoints in your code. Note that when you are using Visual J++ to debug, if you set a breakpoint in a Java source file before starting the debugging session, Visual J++ may not stop on the breakpoint. For performance reasons, the debugger preloads only the main class of your project. The main class is either the class with the same name as the project or the class you specify in Visual J++. If you use the editor to set breakpoints in other classes before the classes are loaded, the breakpoints are disabled.

You can choose one of the following options to load the correct class so that the debugger stops at breakpoints.

  • Select the class in the category Additional Classes, located on the Debug tab of the Project Settings dialog box, and make sure the first column is checked. This loads the class when the debugging session starts.

  • Right-click a method in the ClassView pane of the Project Workspace and select Set Breakpoint from the Shortcut menu. This causes a break when program execution enters the method.

  • Set the breakpoint after Visual J++ has loaded the class during debugging. You may need to step through your Java source until the class is loaded.

When a method has one or more overloaded versions and shows up as a called method in the Call Stack window, the type and value for the parameters are not displayed in some cases. It appears as though the method takes no parameters. This occurs when the called method is not defined as the first version of the overloaded method in the class definition. For example, see the following class definition:

public class Test
   {
       int method(short s)
       {
           return s;
       }

       int method(int i)
       {
           return i;
       }
   }

If you were looking at a call to the second version of the method in the Call Stack window, it would appear without the type and value for the method:

method()

To view the method’s parameters, change the order of the method overloads so that the method that you are currently debugging is first in the class definition.

printf-style Debugging

You can use printf-style debugging to debug your Java classes without using a debugger. printf-style debugging involves including status text messages into your code, allowing you to "step through" your code without a debugger. You can also use printf-style debugging to return error information. The following code shows how you can add a System.out.println call to the try clause of the Hellojtx.HelloObj.SayHello sample.

try
{
System.out.println("This message is from the HelloObj implementation");
   result[0] = "Hello from simple MTS Java sample";
   MTx.GetObjectContext().SetComplete();
   return 0;
}

The client must be a Java client class, and you must use the JVIEW console window to run that class. Note that you need to configure your component to run in the process of its caller, which is in this case JVIEW. Otherwise, this debugging technique results in your component running in the MTS server process (mtx.exe), which would put the println output in the bit bucket rather than the JVIEW console window.

Use the MTS Explorer to configure your component to run in the caller's process by following these steps.

  1. Right-click the component.

  2. Click the Properties option.

  3. Click the Activation tab and clear the In a server process on this computer checkbox.

  4. Select the In the creator’s process... checkbox.

  5. Reload the Client class. Your component's println calls will be visible in the JVIEW console window.

Using the AWT Classes

You can also use the AWT (Abstract Window Toolkit) classes to display intermediate results, even if your component is running in a server process. The java.awt package provides an integrated set of classes to manage user interface components such as windows, dialog boxes, buttons, checkboxes, lists, menus, scrollbars, and text fields.

The following example demonstrates how to use the AWT classes to display intermediate results in a dialog box:

import java.awt.*;

public final class MyMessage extends Frame
{

   private Button closeButton;
   private Label textLabel;

   // constructor
   public MyMessage(String msg)
   {
      super("Debug Window");

      Panel panel;

      textLabel = new Label (msg, Label.CENTER);
      closeButton = new Button ("Close");

      setLayout (new BorderLayout (15, 15));
      add ("Center", textLabel);

      add ("South", closeButton);

      pack();
      show();
   }

   public boolean action (Event e, Object arg)
   {

      if (e.target == closeButton)
      {
         hide();
         dispose();
         return true;
      }

      return false;
   }

}

Asynchronous Java Garbage Collection

Note that garbage collection for Java components is asynchronous to program execution and can cause unexpected behavior. This behavior especially affects MTS components that perform functions such as enumerating through the collections in the catalog because the collection count will be too high (garbage collection is not synchronized). To force synchronous release of references to COM or MTS objects, you can use the release method defined in class com.ms.com.ComLib.

Example:

Import com.ms.com.ComLib
…
ComLib.release(someMTSObject);

This method releases the reference to the object when the call is executed. Release the object reference when you are sure that the reference is no longer needed. Note that if you fail to release the reference, an application error is not returned. However, an incorrect collection count results because the object reference is released asynchronously when the garbage collector eventually runs.

You can also force the release of your reference and not call that released reference again.

Example:

myHello = null;
   System.gc();

Note that this method of forcing the release of an object reference consumes extensive system resources, and is not guaranteed to work in all cases. For more information, please see Microsoft Knowledge Base article number Q179062 available at http://www.microsoft.com/kb/.

It is recommended that you use the release method defined in com.ms.com.ComLib class to release references to MTS objects in a synchronous fashion.


Automating MTS Deployment

This document describes how you can use the scriptable administration objects to automate deployment and distribution of your MTS packages. The MTS Explorer lets you configure and deploy packages by using a graphical user interface rather than by programming code. However, you can use the scriptable administrative objects to automate administration tasks, such as program configuration and deployment. Note that the scriptable administrative objects support the same collection hierarchy as the MTS Explorer. The following figure shows the MTS Explorer collection hierarchy.

For more information about MTS Explorer functionality, see the Administrator's Guide.

Using the Scriptable Administration Objects

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

  • Installing a Pre-Built 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

    Note that you can use the scriptable administration objects to automate any task in the MTS Explorer.

The scriptable administration objects are derived from the IDispatch interface, so you can use any Automation language to develop your package, such as Microsoft® Visual Basic® version 5.0, Microsoft Visual C++® version 5.0, Microsoft Visual Basic® Scripting Edition (VBScript), and Microsoft JScript™.

Each folder in the MTS Explorer hierarchy corresponds to a collection stored in the catalog data store. The following scriptable objects are used for administration:

  • Catalog

  • CatalogObject

  • CatalogCollection

  • PackageUtil

  • ComponentUtil

  • RemoteComponentUtil

The Catalog, CatalogObject, and CatalogCollection scriptable objects provide top-level functionality such as creating and modifying objects. The Catalog object enables you to connect to specific servers and access collections. Call the CatalogCollection object to enumerate, create, delete, and modify objects, as well as to access related collections. CatalogObject allows you to retrieve and set properties on an object. The Package, Component, Remote Component, and Role objects enable more specific task automation, such as installing components and exporting packages. This utility layer allows you to program very specific tasks for collection types, such as associating a role with a user or class of users.

The following diagram illustrates how the MTS scriptable administration objects interact with the MTS Explorer catalog:

Interface | Description
ICatalog | The Catalog object enables you to connect to specific servers and access collections.
ICatalogCollection | The CatalogCollection object can be used to enumerate objects, create, delete, and modify objects, and access related collections.
ICatalogObject | The CatalogObject object provides methods to get and set properties on an object.
IPackageUtil | The IPackageUtil object enables a package to be installed and exported within the Packages collection.
IComponentUtil | The IComponentUtil object provides methods to install a component in a specific collection and to import components registered as an in-process server.
IRemoteComponentUtil | You can use the IRemoteComponentUtil object to program your application to pull remote components from a package on a remote server.
IRoleAssociationUtil | Call methods on the IRoleAssociationUtil object to associate roles with a component or component interface.

For example, you can automate creating a new package and installing components into the new package by using the scriptable objects in the utility layer (Package, Component, Remote Component, and Role objects).

The following Visual Basic sample shows how to use the scriptable administration objects to create and install components into a new package named "My Package."

  1. Declare the objects that you will be using to create and install components into a new package.
    Dim catalog As Object
Dim packages As Object
Dim newPack As Object
Dim componentsInNewPack As Object
Dim util As Object
  2. Use the On Error statement to handle run-time errors if a method returns a failure HRESULT. You can test and respond to MTS trappable errors using the On Error statement and the Err object.
    On Error GoTo failed
  3. Call the CreateObject method to create an instance of the Catalog object. Retrieve the top level Packages collection from the CatalogCollection object by calling the GetCollection method. Then call the Add method to add a new package.
    Set catalog = CreateObject("MTSAdmin.Catalog.1")
Set packages = catalog.GetCollection("Packages")
Set newPack = packages.Add
Dim newPackID As String
  4. Set the package name to "My Package" and save changes to the Packages collection.
    newPackID = newPack.Key
newPack.Value("Name") = "My Package"
packages.savechanges
  5. Call the GetCollection method to access the ComponentsInPackage collection. Then instantiate the ComponentUtil object in order to call the InstallComponent method to populate the new package with components.
    Set componentsInNewPack =
    packages.GetCollection("ComponentsInPackage",
    newPackID)
Set util = componentsInNewPack.GetUtilInterface
util.InstallComponent"d:\dllfilepath", "", ""
Exit Sub
  6. Use the Err object to display an error message if the installation of the package fails.
    failed:
    MsgBox "Failure code " + Str$(Err.Number)

End Sub

For a complete description of how to program these procedures and more sample code, refer to the Administrator’s Guide.


MTS Error Diagnosis

This topic describes how to determine the source of an error in your MTS application. You can diagnose the source and obtain a description of application errors by using a combination of Microsoft® Windows NT®, MTS, and other tools. If you discover that the application error is caused by MTS, you can interpret the error message using the Win32 (Win32.h) or MTS header files (mts.h), or the Microsoft Visual C++® error utility.

For more information on debugging an MTS application, see Debugging MTS Components.

Finding the Source of the Error

If your server application is failing or causing unexpected behavior, you must first determine where your error occurred. Windows NT provides a system Event Viewer that tracks application, security, and system events. Refer to the Application Log in the Event Viewer first to check the application associated with the event message. (Because you can also archive event logs, you can track an event history of the error.) Selecting an entry in your log activates an Event Detail, which provides further information about the system event. If you attempt to run the Sample Bank client without starting the Microsoft Distributed Transaction Coordinator (MS DTC), you will be returned the following Automation error.

Since this error does not indicate which application caused the failure, you can reference the Application Log in the Event viewer, which shows the error was caused by MTS.

Note If you are using MTS for Windows 98, events are written to text files in the \Windows\MTSLogs directory.

Interpreting Error Messages

The Event Viewer helps you determine the application source of the problem. You can use other tools to interpret individual error messages. Success, warning, and error values are returned using a 32-bit number known as a result handle, or HRESULT. HRESULTs are 32-bit values with several fields encoded in the value. A zero result indicates failure if that bit is set. A non-zero result can be a warning or informational message.

HRESULTs work differently, depending on the platform you are using. On 16-bit platforms, an HRESULT is generated from a 32-bit value known as a status code, or SCODE. On 32-bit platforms, an HRESULT is the same as an SCODE. Note that if you are returning HRESULTs from Java, you should throw an instance of com.ms.com.ComFailException to indicate failure. You can specify a particular HRESULT when constructing the ComFailException object. The HRESULT is used as the return value for the COM method. To indicate successful completion, you do not need to do anything; just return normally. To return S_FALSE, indicating a successful completion but a return value of Boolean False, throw an instance of com.ms.com.ComSuccessException. In Visual Basic, you use the Err.Raise function to set and the On Error… / Err.Number to retrieve HRESULTs.

For a list of the values of common system-defined HRESULTs, see ComFailException. For a complete list of system-defined HRESULT values, see the header file Winerror.h included with the Platform SDK.

MTS never changes the value of an HRESULT error code, such as E_UNEXPECTED or E_FAIL, returned by an MTS object method. When an MTS object returns an HRESULT status code (such as S_OK or S_FALSE), MTS may convert the status code into an MTS error code before it returns to the caller. This occurs, for example, when the application returns S_OK after calling the SetComplete function; if the object is the root of an automatic transaction that fails to commit, the HRESULT is converted to CONTEXT_E_ABORTED. When MTS converts a status code to an error code, all the method's output parameters are cleared. Returned references are released and the values of the returned object pointers are set to NULL.

The Mtx.h header file contains the MTS specific error codes. Winerror.h contains the error code definitions for the Win32 API. For an overview of error codes, see "Error Handling" in the COM portion of the Microsoft Platform SDK

You can also use the ERRLOOK utility in Microsoft Visual Studio™ 97 to retrieve a system error message or module error message based on the value entered. ERRLOOK retrieves the error message text automatically if you drag-and-drop a hexadecimal or decimal value from the Visual Studio debugger or other Automation-enabled application. You can also enter a value either by typing it in or pasting it from the IDE clipboard and clicking the Look Up option.

Contacting MTS Support

If you run into a problem that you cannot solve, you can contact Microsoft support with the following information:

  • Topography of the application where the error occurred, such as a description of packages, components, and interfaces

  • Application event log on all computers

  • Reproduction of the error, if possible

Troubleshooting

If you are having trouble diagnosing your problem, refer to the list of troubleshooting tips below:

  • Make sure that the Distributed Transaction Coordinator (DTC) is running on all servers.

  • Check network communication by first testing on a local computer to verify that the application works. If you are running TCP/IP on your network, you can then use the Windows NT Ping.exe utility to verify that the machines are on the network.

  • Make sure that SQL and DTC are either located on the same computer or that the DTC Client Configuration program specifies that the DTC is on another computer. If not, SQLConnect will return an error internally when called from a transactional component.

  • Set the MTS transaction timeout to a higher number than the default 60 seconds; otherwise, during debugging, MTS aborts the transaction after this time has lapsed. All subsequent calls to the component return immediately with CONTEXT_E_ABORTED.

Note Any changes to the transaction timeout value made during debugging may not take effect until you restart your debugging environment.

  • Make sure that your ODBC drivers are thread-safe and do not have thread affinity.

  • If you have difficulty getting an application to work over several servers, reboot the client and then verify that your Windows NT domain controller is configured properly.

  • Turning off resource pooling may reveal that a resource dispenser used by your application is the source of the problem. You can turn off resource pooling by setting the following registry key:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Transaction Server\Local Computer\My Computer:Resource Pooling=N

See the DCOM documentation and DCOM-related Knowledge Base articles if you are experiencing application errors that you suspect are caused by DCOM.


Microsoft Transaction Server Transactional Component Services

Microsoft Transaction Server
Transactional Component Services

A Guide to Reviewing
Microsoft Transaction Server
Release 2.0—The Best Technology
for Developing and Deploying
Applications on Windows NT

Abstract

Microsoft® Transaction Server Release version 2.0 (MTS) is an important feature of the Microsoft® Windows NT® operating system that simplifies the development and deployment of server-centric applications built using Microsoft Component Object Model (COM) technologies. MTS is ideal for developing line-of-business and electronic commerce applications with Web-based interfaces, leveraging powerful development tools such as the Microsoft® Visual Basic® programming system for scalable, three-tiered development, and building production-quality applications with distributed object technologies.

MTS 1.0 revolutionized the way that developers build server-centric applications by providing a true component-oriented run-time environment that eliminates writing infrastructure code. MTS 2.0 extends the environment by providing powerful integration with Microsoft Internet Information Server 4.0 (IIS), transactional connectivity to Oracle and DB2 databases, integration with Microsoft® Message Queue Server 1.0 (MSMQ), transactional mainframe connectivity via Microsoft® SNA Server 4.0 and the COM Transaction Integrator, and improved performance and manageability.

For the latest information on Microsoft Transaction Server, visit http://www.microsoft.com/com/mts-f.htm.

Microsoft Transaction Server Release 2.0
Trends and Needs

The argument for three-tiered systems is deceptively simple: by partitioning applications cleanly into presentation, application logic, and data sections, the result will be enhanced scalability, reusability, security, and manageability. And, three-tiered approaches map well to a number of important application development challenges. Internet and intranet application developers need easy ways to use browsers as the presentation interfaces to line-of-business and electronic commerce applications.

Developers that use powerful programming tools such as Visual Basic and the Microsoft® Visual J++® Web development system to build two-tiered systems now want to build more scalable and flexible applications without having to completely change the way they write code. Designers of distributed component-based applications need ways to deploy and reuse objects anywhere in a network, from desktop machines to servers, without sacrificing security, scalability, or manageability. Yet, despite this nearly universal desire, most companies have made very little progress in harnessing the power of three-tiered, server-centric architectures.

Challenges

Three-tiered architectures are often called server-centric, because they enable application logic to run on servers instead of on clients as in two-tiered applications. In fact, to deliver maximum benefit, three-tiered systems must run with presentation logic on desktop clients or browsers, application logic on middle-tiered servers, and data on dedicated database servers. This presents a number of challenges:

  • Popular development tools generate two-tiered applications that have presentation logic and application logic bundled tightly together in the client and operate by accessing database servers directly over network connections. Deploying two-tiered applications in Web environments means placing presentation and application logic in the browser and accessing corporate data over the Internet. This effectively puts application logic and data outside corporate firewalls.

  • Conventional products that provide scalable three-tiered infrastructures, such as Distributed TP Monitors, are extremely complex and offer very limited support for using tools such as Visual Basic to write server-based application logic.

  • Popular CORBA-compliant Object Request Brokers (ORBs) offer only basic support for true component architectures, lack production-quality run-time infrastructures, and limit access to existing applications and data. When objects are deployed on middle-tiered servers, reusability, security, scalability, and connectivity suffer.

Complicating matters, companies frequently need to buy expensive and complex add-in software from third-party vendors to address these issues. This creates dependencies on third parties and makes developers integrate products from different vendors—increasing the development and maintenance costs of distributed applications. While these issues affect all software developers, add-in costs and dependencies are particularly relevant to independent software vendors (ISVs) who must minimize external costs and maintain control over product quality.

Solution Requirements

To meet the challenges, any solution must satisfy three technical requirements:

  • It must be easy to deploy presentation components on browsers and position application components and corporate data safely behind firewalls.

  • It must be easy to use powerful tools such as Visual Basic and Microsoft® Visual J++® to build the presentation and application logic components of three-tiered applications.

  • Production-quality run-time services, such as security support, database connection pooling, and application management facilities must be an integral part of the server-based infrastructure. Components must also be able to access existing applications and data with full transaction protection.

In addition to technology requirements, solutions must be cost-effective, reduce vendor dependency, and minimize the need for systems integration.

Solution: Microsoft Transaction Server

A Three-Tiered Application

Microsoft Transaction Server (MTS) Release 2.0 is an important feature of Windows NT that simplifies the development and deployment of server-centric applications built using Microsoft Component Object Model (COM) technologies. MTS is ideal for developing line-of-business and electronic commerce applications with Web-based interfaces, leveraging powerful development tools such as Visual Basic for scalable, three-tiered development, and building production-quality applications with distributed object technologies. MTS 2.0 is the best technology for building server-centric applications on Windows NT because:

  • MTS 2.0 is the easiest way to build and deploy COM-based applications on Windows NT. Developers can use popular development tools such as Visual Basic. The true component-based architecture of MTS simplifies object use and reuse, and administrators perform management tasks via graphical, drag-and-drop user interfaces.

  • MTS 2.0 offers comprehensive component functionality such as automatic transaction support, simple but powerful role-based security, access to popular databases, message queuing products and mainframe-based applications, and performance-enhancing features such as database connection pooling.

  • MTS 2.0 is fully integrated with other Windows NT features such as IIS and Active Server Pages (ASP) for ease of Internet/intranet application development, Windows NT clustering services for failover protection, MSMQ for reliable, loosely coupled communication, and the Windows NT security environment for simplified resource control.

MTS 1.0 revolutionized the way that developers build server-centric applications by providing a true component-oriented run-time environment that eliminates the need to write infrastructure code. MTS 2.0 now extends the environment by providing powerful integration with Microsoft Internet Information Server 4.0 (IIS), transactional connectivity to Oracle and DB2 databases, integration with Microsoft Message Queue Server 1.0 (MSMQ), transactional mainframe connectivity via Microsoft SNA Server 4.0 and the COM Transaction Integrator (COMTI), and improved performance and manageability.

Most important, MTS 2.0, IIS 4.0, MSMQ 1.0, and SNA Server 4.0 are available and integrated today, in contrast to the mixture of specifications, plans, and poorly integrated products available from other vendors.

What’s New in MTS 2.0

Microsoft Transaction Server—Release 2.0 Features at a Glance

Feature Description and Benefits

Easy

Ease-of-Use |
MTS Explorer Enhancements | The MTS 2.0 Explorer includes enhanced support for managing packages and ease-of-use enhancements such as support for multiple item selection. The MTS explorer can also manage IIS applications and components generated by SNA Server 4.0 as MTS packages.
Microsoft Management Console Support | MTS 2.0 provides support for running the MTS Explorer management interface as a snap-in to the Microsoft Management Console (MMC). MMC integration provides users with a consolidated management environment with a consistent look and feel.

Comprehensive

Automatic Client Installation Utility | MTS 2.0 provides a utility that simplifies and automates the process of configuring client machines that need to access MTS-based components running on a server.
Comprehensive Functionality |
Transactional ODBC Driver for Oracle and DB2 Databases | MTS 2.0 includes ODBC 3.0-compliant drivers for Oracle version 7.3 and later and IBM's DB2. ODBC 3.0 compliance enables developers to build MTS applications that access these databases (on any platform), along with access to other resources such as SQL Server™ and Microsoft® Message Queue Server, in transactional units of work for full data integrity protection. ODBC 3.0 compliance also lets MTS perform connection pooling of Oracle and DB2 database sessions for improved performance.
Performance Enhancements | MTS 2.0 provides a multi-threaded class factory that reduces the time it takes for MTS to create and execute instances of server-based objects when requested by clients. MTS 2.0 also improves performance by reducing the number of steps that many internal operations require for execution.
Windows NT Workstation and Windows 95 Support | MTS 2.0 supports operation on Windows NT Workstation and the Microsoft® Windows® 95 operating system to let companies deploy stand-alone versions of their MTS applications.

Integrated

Integration with Windows NT Features and Microsoft Products |
IIS Integration | Microsoft Internet Information Server (IIS) version 4.0 is integrated with MTS 2.0 and uses MTS for many run-time services such as transaction management. Transaction support lets IIS Active Server Pages access databases, mainframe applications, and message queues with full data integrity protection. MTS integration also provides IIS with process isolation to prevent individual failures from affecting other parts of a Web site, enhanced run-time services such as thread and connection pooling for better performance, and easier component management.
MSMQ Integration | MTS 2.0 provides integration with Microsoft Message Queue Server (MSMQ). MSMQ integration lets MTS-based applications communicate in a reliable, loosely coupled fashion. MSMQ operations (such as send and receive) enlist in MTS transactions automatically for data integrity protection.
Microsoft Transaction Server—Release 2.0 Features at a Glance |
Feature | Description and Benefits
Integration with Windows NT Features and Microsoft Products |
SNA Server 4.0 Integration | MTS 2.0 can use Microsoft SNA Server 4.0 and the COM Transaction Integrator to include mainframe applications (e.g., running under CICS) and their data in transactions managed by MTS. MTS-based applications can update one or more databases, send or receive messages with MSMQ, and invoke a mainframe application that updates data (e.g., in VSAM), and all updates, including those on the mainframe, will commit or abort as a whole. Microsoft SNA Server 4.0 also makes it easy to generate COM-based interfaces to mainframe applications for simplified MTS programming.
Support for Windows NT Clustering Services | MTS 2.0 supports operation on clustered Windows NT Servers. Clustering lets administrators configure MTS for automatic failover and fault-tolerant, high-availability operation.

MTS Complete Features at a Glance

Microsoft Transaction Server—Complete Features at a Glance |
Feature | Description and Benefits

Easy

Ease-of-Use |
Simplified Three-Tiered Application Development | MTS completely separates three-tiered infrastructure from presentation and application logic. This lets MTS developers build their applications as a collection of single-user COM components and then deploy components in the appropriate tier. Components that run on middle-tiered servers automatically support multi-user operation.
COM Component Support | MTS supports any tool that can produce COM DLLs, including Microsoft Visual Basic, Microsoft® Visual C++® development system, and Microsoft Visual J++.
Simple Application Programming Interfaces | MTS developers have to learn only a simple set of APIs to deploy their components in the MTS run-time environment. Extensive knowledge of COM APIs and Microsoft® Win32® APIs is not required.
Support for Component Packages | MTS lets developers combine related components into packages. Packages provide an easy way to secure, manage, and deploy components as a group.
MTS Explorer | The MTS Explorer is a GUI-based systems management console that makes it easy for administrators to create and manage packages, configure component properties such as security and transaction behavior, and monitor/manage operating servers. The MTS Explorer supports advanced ease-of-use features such as drag-and-drop editing and multiple item selection.
Microsoft Management Console Support | The MTS Explorer management interface can run as a snap-in to the Microsoft Management Console (MMC). MMC integration provides users with a consolidated management environment with a consistent look and feel.

Comprehensive

Automatic Client Installation Utility | MTS provides a utility that simplifies and automates the process of configuring client machines that need to access MTS-based components running on a server.
Comprehensive Functionality |
Automatic Transaction Management | MTS provides automatic transaction management services to components. Developers or administrators use simple property page interfaces to specify what level of transaction protection is required by a given component. At run time, MTS performs all required transaction management automatically. This dramatically simplifies development and facilitates component reuse.
Database Connection Pooling | MTS allocates connections to database resources from a pool. Components only require connections when they are active. This lowers the total number of database connections required to support clients, increasing database performance.

Microsoft Transaction Server—Complete Features at a Glance

Feature | Description and Benefits
Comprehensive Functionality (Cont.) |
Transactional ODBC Driver for Oracle and DB2 Databases | MTS includes an ODBC 3.0-compliant driver for Oracle version 7.3 and later and DB2. ODBC 3.0 compliance lets developers build MTS applications that access these databases (on any platform), along with access to other resources such as SQL Server and Microsoft Message Queue Server, in transactional units of work for full data integrity protection. ODBC 3.0 compliance also lets MTS perform connection pooling of Oracle and DB2 database sessions for improved performance.
Support for Multiple Databases and Resource Managers | Components running under MTS can access multiple databases and other resources such as message queuing systems and mainframe applications, simultaneously and with full data integrity protections. MTS automatically manages transactions so that all databases and other resources included in a request commit or abort as a unit.
No MTS Client Footprint | MTS applications require no libraries or run-time environment on client machines beyond that provided by COM and Distributed COM (DCOM).
Windows NT Workstation and Windows 95 and Windows 98 Support | MTS supports operation on Windows NT Workstation and Windows 95 and Windows 98 so that companies can deploy stand-alone versions of their MTS applications.
Process Isolation | MTS Administrators can configure packages to run in their own process to ensure that failures do not spread to other packages.
Automatic Thread Pooling | As requests come from clients, MTS automatically assigns threads to components from a pre-allocated pool. When a component finishes executing, MTS reclaims the thread. This reduces the overhead of thread creation/deletion for better performance.
Automatic Object Instance Management | MTS creates instances of components on the server only when requested and reclaims memory resources when the component finishes executing. This form of just-in-time (JIT) activation and as-soon-as-possible deactivation minimizes memory requirements on server machines.
Shared Property Manager | MTS provides the Shared Property Manager to simplify programming of components that need to save state information or share state with other components.

Integrated

Integration with Windows NT Features and Microsoft Products |
Integration with Microsoft SQL Server 6.5 | Microsoft SQL Server 6.5 provides an OBDC 3.0 driver that provides extremely fast and efficient transactional integration, based on Microsoft Distributed Transaction Coordinator (DTC) to MTS applications. Because MTS also uses DTC for transaction management, administrators can use the same utilities to manage transactions between MTS and SQL Server, simplifying systems administration

Microsoft Transaction Server—Complete Features at a Glance

Feature | Description and Benefits
Integration with Windows NT Features and Microsoft Products (Cont.) |
IIS Integration | Microsoft Internet Information Server version. 4.0 is integrated with MTS and uses MTS for many run-time services such as transaction management. MTS integration makes it easy for IIS Active Server Pages to call MTS components that access databases, mainframe applications, and message queues with full data integrity protection. MTS integration also provides IIS with process isolation to prevent individual failures from affecting other parts of a Web site, enhanced run-time services such as thread and connection pooling for better performance, and easier component management.
MSMQ Integration | MTS provides integration with Microsoft Message Queue Server (MSMQ). MSMQ integration lets MTS-based applications communicate in a reliable, loosely coupled fashion. MSMQ operations (such as send and receive) enlist in MTS transactions automatically for data integrity protection
SNA Server 4.0 Integration | MTS can use Microsoft® SNA Server 4.0 and the COM Transaction Integrator to include mainframe applications (e.g., running under CICS and IMS) and their data in transactions managed by MTS. MTS-based applications can update one or more databases, send or receive messages with MSMQ, and invoke a mainframe application that updates data (e.g., in VSAM), and all updates, including those on the mainframe, will commit or abort as a whole. Microsoft SNA Server 4.0 also makes it easy to generate COM-based interfaces to mainframe applications for simplified MTS programming.
Support for Windows NT Clustering Services | MTS supports operation on clustered Windows NT Servers. Clustering enables administrators to configure MTS packages for automatic failover and fault-tolerant, high-availability operation.
Windows NT Security Integration | MTS provides a role-based security mechanism that is integrated with the Windows NT security environment. Roles let developers design security protections into their components without knowing how the components will be ultimately deployed. Administrators map roles to users when they install components into MTS.

Availability and Licensing

Availability

MTS 2.0 is included as a feature of Microsoft Windows NT Server, Enterprise Edition 4.0, Microsoft Windows NT Server, Standard Edition 4.0, Windows NT Workstation 4.0, and Windows 95. In addition, existing licensees of these operating system platforms can obtain MTS 2.0 via the Windows NT 4.0 Option Pack. The Option Pack is available in CD-ROM form for an estimated retail price of US $99 or via a Web-based download for no charge.

Licensing

Machines that invoke MTS-based components running on Windows NT Server require a Microsoft Windows NT Server Client Access License (CAL). Machines that already have an existing CAL for access to services such as print services and file sharing do not need to purchase an additional CAL in order to use MTS Clients. Visit http://www.microsoft.com/ntserver for more information on Microsoft Windows NT Server licensing.

Introduction to
Three-Tiered Computing

Every application, from spreadsheets on personal computers to accounts payable systems on mainframes, has three components: presentation, application logic, and data. Presentation components focus on interacting with the user. Application logic components perform calculations and determine the flow of the application. Data components manage information that must persist across sessions, planned shutdowns, and systems failures. While every application has these components, application developers have many configuration and deployment options that balance issues such as development costs with scalability and component reusability.

Two-Tiered Architectures and Their Strengths

A Two-Tiered Application

Two-tiered applications group presentation and application logic components together on the client machine and access data remotely via network connections. Using tools such as the industry-standard Structured Query Language (SQL), application components on the client read data from the database, perform application logic, and send data back to the database when updates need to occur. Two-tiered applications usually work very well in departmental-scale applications with modest numbers of users (under 100), a single database, and secure, fast networking. And, because most client/server development tools automatically generate them, two-tiered applications are the most common form of network-based application in use today.

Two-Tiered Limitations

With a large number of users, multi-database environments, and non-secure network environments, two-tiered applications can experience a number of limitations.

  • Databases must maintain connections to each active client. Connections consume machine resources, reducing performance as the number of clients rises.

  • Contention can occur in the database as many clients try to work on the same data concurrently. To prevent corruption, once one client has asked for access to a specific piece (e.g., a row) of data, databases lock the data to prevent other clients from accessing the same data at the same time. Other clients must wait for the database to release the locks before they can proceed with their work.

  • The security model used in two-tiered systems doesn’t work well outside of trusted LAN environments. Two-tiered security focuses on granting or denying users access to data. Once administrators give a user access to a table (presumably for legitimate reasons), the user can do virtually anything to the data.

  • It is difficult to reuse two-tiered application logic broadly, because applications are tightly bound to specific database systems and table formats. Reusing two-tiered application logic usually means cutting and pasting code between applications.

  • Two-tiered systems can only access one database system at a time. Access to other database systems, mainframe applications, or other resources must be done via gateways that, in turn, create a number of new issues (see below).

Developers also need to be aware that because of lock contention, two-tiered applications scale well to a point and then can degrade quickly. And because lock resolution is not dependent on the speed of the server, even installing more powerful database machines does not provide significantly greater performance.

Two-and-a-Half–Tiered Architectures and Their Strengths

A Two-and-a-Half–Tiered Application
with Data Gateways

When companies experience the limitations of two-tiered models, most turn to data gateways and database stored procedures. Data gateways enable data that resides in other databases (e.g., VSAM data on a mainframe) to look and behave as though it resides in the database to which the client is connected. This increases the number of sources of data that a two-tiered application can access and offers an appealing way to integrate different applications via extending access to their data.

A Two-and-a-Half–Tiered Application
with Stored Procedures

Stored procedures provide a way for clients to execute groups of SQL statements remotely on the database server. Client applications invoke stored procedures by name, and can pass parameters in to, and retrieve results out of, the procedures. Stored procedures reduce network traffic by minimizing the data that must be exchanged back and forth with the client, improve security by limiting use to authorized clients, and improve application reuse by enabling multiple presentation components to call the same stored procedure centrally. Stored procedures also improve scalability. As a rule of thumb, applications that use stored procedures can support about twice as many users as similar two-tiered applications.

Where two-tiered architectures group presentation and application components together on the client, stored procedures group the application logic components with the data components on the database server. This is often called a two-and-a-half–tiered architecture, because stored procedures offer greater scalability, security, and reuse than two-tiered approaches, but are not as powerful as true three-tiered architectures.

Two-and-a-Half–Tiered Limitations

Despite the advantages of data gateways and stored procedures, a number of significant limitations exist. Regarding stored procedures:

  • Individual stored procedures implementations offer limited reuse, because they lack component features such as multiple interface support and externalized security and transaction properties.

  • Stored procedure mechanisms from different database vendors are incompatible, so application logic cannot be reused outside of a single database system.

  • Stored procedure approaches require a database connection for each client and consume database machine resources when executing, reducing performance as the number of clients increases.

Regarding data gateways, integration with other applications must occur at the data-to-data level via gateways. Implications of data-to-data integration include:

  • In order for data gateway architectures to work, the owners of all other applications (which may include business partners) must be willing to ship their data (via gateways) to applications outside their control. They must also allow their data to be modified by those applications and then be shipped back and reinserted in the database.

  • Changes to the layout of a table or associated business rule can affect many other applications that have to be updated before they can operate properly.

  • If any of the sources of data are not relational databases (such as VSAM data on mainframes), gateways will have to convert their data into SQL tables first, which slows performance dramatically.

Based on these limitations, stored procedures work best within single applications that need enhanced scalability and security, but do not require broad reuse. Data gateways work best for read-only and very low volume applications. Applications that require component reuse and the ability to access multiple databases and mainframe applications clearly need something more.

Three-Tiered Architectures

A Three-Tiered Application

In three-tiered architectures, presentation, application logic, and data components are separated into distinct units. Presentation components manage user interaction and make requests for application services by calling middle-tiered components. Application components perform business logic and make requests to databases and other resources using their native interfaces such as SQL to a database or LU6.2 to mainframe applications. Clients can call as many server-based components as they need to complete a request, and components can call other components to improve reuse.

Three-Tiered Strengths

Three-tiered architectures are often called server-centric, because they uniquely enable application components to run on middle-tiered servers independent of both the presentation interface and database implementation. While there is no requirement that each unit runs on a different physical machine, to deliver maximum benefit, three-tiered systems must run with presentation components on desktop clients or browsers, application logic on middle-tiered servers, and data on dedicated database servers. The independence of application logic from presentation and data offers many benefits:

  • Developers can use powerful development tools such as Visual Basic and Visual J++ to develop reusable application components, instead of using more limited stored procedure languages.

  • Administrators can replicate application components to run on multiple machines simultaneously. This spreads client loads across multiple machines and enables higher availability, scalability, and performance. Application component replication (as opposed to data replication) is not possible with two-and-a-half–tiered architectures, because stored procedures must run in a single database.

  • Application components can share database connections. This lowers the number of total sessions that the database server must support, improving performance. With two-tiered and two-and-a-half–tiered systems, the database must allocate a connection for every user.

  • Middle-tiered application components can be secured centrally using a common infrastructure. Access can be granted or denied on a component-by-component basis, simplifying administration.

  • Access to resources such as mainframe applications and other databases are via native protocols and application interfaces instead of via data gateways. This improves performance and allows application owners to control access to their data.

While the arguments for three-tiered architectures are compelling, most companies have little experience beyond two-tiered and two-and-a-half–tiered approaches. The most likely explanation is that development tool support for database-centric approaches is strong and few tools have existed to assist with server-centric development. With the availability of Microsoft Transaction Server, supported by a wide range of popular development tools, three-tiered development is finally accessible to a wide range of companies.

MTS Overview

MTS Application Architecture

MTS provides a server-centric environment for developing and deploying three-tiered applications based on Microsoft’s Component Object Model (COM) technologies. In MTS applications, application logic components run under the control of MTS on servers and are invoked by presentation-centric components running on clients via Microsoft’s Distributed COM (DCOM) technologies. Clients can be a mixture of conventional applications and Active Server Page (ASP) scripts running within Microsoft Internet Information Server (IIS).

Application logic components can access a number of different databases, Microsoft Message Queue Server (MSMQ), and, via Microsoft SNA Server 4.0, CICS and IMS applications. Access to databases and resources is done through MTS Resource Dispensers that perform services such as connection pooling automatically. MTS also supports automatic transactions so that access to data and resources is done with all-or-nothing protection.

Developers can build presentation and application logic components with any tool or programming language that can generate COM-compliant dynamic-link libraries (DLLs). For components to work efficiently within the MTS environment, developers must observe several simple rules:

  • Components must create a reference to their MTS Context Object by making a simple API call. Creating the reference enables the component to take advantage of MTS services such as transaction and security support.

  • Do not save state information across transaction boundaries within components (e.g., in local or global variables). Components that save state are less scalable, because MTS cannot recycle their resources when they finish executing. State should be kept in databases or in the Shared Property Manager (SPM) in MTS and retrieved by components when needed.

    A Sample MTS Component Code Template

  • When a component completes execution successfully, it must call the SetComplete method on the MTS Context Object. This tells MTS that this component wishes to commit any work it has performed when all components involved in the transaction finish executing. Calling SetComplete also tells MTS that it can recycle any resources held by the component.

  • If a component cannot complete executing successfully, it must call the SetAbort method on the MTS Context Object. This tells MTS that it should abort the current transaction and roll back all changes made by components involved in the transaction. Calling SetAbort also tells MTS that it can recycle any resources held by the component.

When components follow these rules, applications can take advantage of MTS benefits such as enhanced scalability, performance, and management with no additional development. Installation of components into the MTS environment is as simple as dragging and dropping the component’s DLL into an MTS Explorer window.

When to Use MTS

MTS represents a powerful technology for developing and deploying three-tiered applications based on Microsoft Component Object Model (COM) technologies. As detailed in the Introduction to Three-Tiered Computing section, three-tiered architectures offer broad benefits, such as enhanced scalability, security, and application logic reuse. Because these benefits are applicable to a variety of applications in all industries, a good way to understand when to use MTS is to describe several common scenarios and show how MTS adds value.

Web-Based Applications

Among its many roles, the Web has emerged as an ideal vehicle for delivering line-of-business and electronic commerce applications to users. Browsers represent an easy way to present powerful and dynamic user interfaces with near-zero desktop footprint and maintenance costs. And Internet/intranet networking now reaches almost every part of the world, making it possible to turn virtually any desktop into a high-powered presentation and user input device instantly.

As noted earlier, presentation and user input are just part of the total application. There needs to be something else that takes the user’s HTML-based input and invokes an application that processes the request against the appropriate databases and other resources. In the case of the Web, that something is usually a Web server. While early Web servers simply provided access to static HTML pages, most Web servers now have mechanisms for accepting input from users, invoking an application or script, and returning a response.

Web-Based Application Challenges

The process of handling application requests from Web servers is more difficult than it sounds:

  • Most Web servers provide little or no application infrastructure themselves. They can invoke an application or script, but it is up to the developer to provide basic services such as database access.

  • Most application development technologies that provide good infrastructure services don’t integrate well with Web servers. For example, a common problem is maintaining information about users between browser requests in the connectionless world of the Web.

Conventional solutions to these issues rely on mechanisms such as Common Gateway Interface (CGI) scripts, which work well enough for basic applications, but are not scalable or robust enough for line-of-business or electronic commerce applications.

The Role of IIS and Active Server Page Scripts

MTS, combined with Microsoft Internet Information Server 4.0 (IIS), is ideal for building production-quality three-tiered applications with Web-based front ends. IIS provides state-of-the-art facilities for delivering visual and interactive pages to browsers and a powerful application mechanism called Active Server Pages (ASPs). ASPs are text files that contain a combination of standard HTML commands and scripts based on Visual Basic and JavaScript.

Web page designers use ASPs by building standard Web pages containing references to URLs with .ASP extensions. For example, it is common to send input forms to users and designate in the return URL an ASP that processes the response. When users fill in the form and send their response, IIS sees that the URL contains a .ASP extension and executes the appropriate ASP script. The script in the ASP file can do basic calculations, access databases via ODBC interfaces, and build an HTML stream to send back to the browser. Most important, ASPs can also call components running under MTS.

MTS Benefits

When an ASP calls an MTS component, the request comes in to MTS as though it came from any other client. The component can:

  • Perform any calculations required by the application logic.

  • Access one or more databases via ODBC 3.0-compliant drivers (e.g., SQL Server 6.5 or Oracle 7.3).

  • Access mainframe applications, using standard COM component interfaces, via SNA Server 4.0.

  • Send or receive a message via Microsoft Message Queue Server (MSMQ). Via bridge products from Level 8 Systems, Inc., MTS components can also use MSMQ to access legacy applications that use IBM’s MQSeries products.

  • Call other components that perform other parts of the application logic, enabling component reuse. In fact, companies can reduce development time by composing applications from a pool of prebuilt and purchased components.

Because MTS provides automatic transaction support, if any error condition occurs within an MTS component or the ASP script, MTS will roll back all changes made to databases (including those on the mainframe, if applicable) and message queues. MTS transactions provide important data integrity protection required by production-quality systems. For example, without transaction protection, orders could be sent without collecting payments, and accounts could get out of balance. MTS also provides a sophisticated component run-time environment that can handle a large number of users and deliver consistently good response times.

Choosing Between ASPs and MTS Components

By combining IIS Active Server Pages with MTS, companies can build powerful line-of-business and electronic commerce applications—out of reusable components—with interactive and dynamic Web-based interfaces. In fact, while ASPs alone can implement significant amounts of calculations and database access, Microsoft recommends using ASPs for presentation management and MTS for application logic. This approach yields Web-based applications, built from reusable components, that can support a large number of users, perform complex calculations, and access multiple databases and other resources such as mainframe applications.

Using Common Tools to Build Three-Tiered Applications

Development tools such as Visual Basic and Visual J++ owe a lot of their popularity to how easy they make building two-tiered applications. Features such as data-bound controls isolate developers from the complexities of database access while providing powerful user interface capabilities. Their support for COM also makes it easy to develop applications using a modular, component-based architecture.

Three-Tiered Challenges

At the same time, two-tiered applications built with Visual Basic and Visual J++ will experience the same limitations as other two-tiered applications. For example, data-bound controls place demands on databases and can create contention issues when used by many users concurrently. It also was difficult (prior to MTS 1.0) to use COM components built using Visual Basic (and other COM-enabled tools) outside of the client environment.

MTS Benefits

With MTS, developers can use their existing skills with tools such as Visual Basic to build sophisticated three-tiered applications. By following a few simple rules, developers can generate the application logic components of their programs as COM-enabled dynamic-link libraries (DLLs) that run on middle-tiered servers under the control of MTS. Then, when client-based presentation components call server-based application components, the Distributed Component Object Model (DCOM) run time will route the requests to MTS automatically. Benefits of using MTS in this environment include:

  • The ability to access databases and other resources such as mainframe applications with full transaction protection.

  • Simplified component deployment via MTS Explorer drag-and-drop interfaces and automated client installation utilities.

  • Automatic use of scalability features such as thread pooling, database connection pooling, and just-in-time (JIT) object instantiation.

  • Easy component management via the MTS Explorer.

  • The ability to call the same objects from IIS and Active Server Pages

With MTS, companies can realize the benefits of powerful development tools on an entirely different scale.

Production-Quality Distributed Object Applications

Companies have been aware of the benefits of object-oriented programming tools and development methodologies for some time. Initially, objects delivered most of their value in client-centric uses, such as user interface development, because companies lacked any standard and easy way to use objects in server-centric roles. MTS changes this.

By providing support for server-side objects, MTS lets companies take advantage of distributed objects in line-of-business applications. Developers build their applications as a collection of COM components and deploy them using MTS. Using a combined COM and MTS approach, developers of distributed object applications will see a number of benefits:

  • A consistent, language-neutral object model that is supported by all tools, including Visual C++ and Visual J++, and products that can generate COM-compliant DLLs. The COM specification is completely public, enabling any vendor to develop COM-compliant products.

  • Sophisticated run-time services such as automatic thread pooling, database connection pooling, and object instance management for excellent performance and scalability.

  • Complete support for transactions and role-based security, managed externally to the object via property page interfaces, which provide data integrity and protection while facilitating component reuse.

  • Integrated support for multiple databases, message queuing systems, and mainframe interoperability, including full transaction protection.

  • Powerful GUI-based systems administration via the MTS Explorer.

Most important, MTS 2.0 and supporting products such as MSMQ 1.0, IIS 4.0, and SNA Server 4.0, are available and integrated today, in contrast to the mixture of specifications, plans, and poorly integrated products available from other vendors.

Focus: Ease-of-Use

Simplified Three-Tiered Application Development

MTS Benefits:
Enables scalable, reusable
applications without complex programming

Works well with popular
development tools

Adds only a simple set of new API calls

Simplifies configuration and management

MTS completely separates three-tiered infrastructure from presentation and application logic. This lets MTS developers build their applications as a collection of single-user COM components and then deploy components in the appropriate tier. Components that run on middle-tiered servers automatically support multi-user operation as long as they follow a few simple rules.

COM Component Support

MTS supports any tool that can produce COM DLLs, including Microsoft Visual Basic, Microsoft Visual C++, and Microsoft Visual J++. Developers and businesses that use these languages for desktop solutions can now use them for server solutions. A number of other tools vendors provide COM support within their products, including:

  • PowerSoft PowerBuilder and Optima++

  • Borland Delphi

  • MicroFocus COBOL

  • Fujitsu COBOL

Simple Application Programming Interfaces

Most existing three-tiered development environments, such as distributed TP monitors, require developers to master a complicated set of programming interfaces. This represents both a significant up-front training investment and a significant ramp-up time before developers become productive. In fact, programming complexity is one of the reasons that Object Request Brokers (ORB) and TP Monitors have been slow to achieve mass market acceptance.

MTS takes advantage of existing COM programming interfaces, familiar to desktop developers, in order to lower training costs and shorten ramp-up time. For example:

  • Programmers using Visual Basic invoke MTS applications with Create Object.

  • Programmers using Visual C++ call MTS applications with CoCreateInstance.

MTS developers have to learn only a simple set of new APIs to deploy their components in the MTS run-time environment. Extensive knowledge of COM APIs and Microsoft® Win32® APIs is not required.

Support for Component Packages

Assembling different software components into a single solution can present configuration and deployment issues. To address these issues, MTS provides the concept of packages to make it easy for developers to combine different COM components into a single manageable unit. Components in a package execute in the same process space, share common security, and can be deployed as a single unit. Packages also increase application deployment flexibility. An entire application can be distributed as a single package and then partitioned into multiple packages to optimize performance, load balancing, and fault isolation.

MTS Explorer

The MTS Explorer is a GUI-based systems management interface that makes it easy for administrators to create and manage packages, configure component properties such as security and transaction behavior, and monitor/manage operating servers. Other MTS Explorer features include:

  • Advanced ease-of-use features such as drag-and-drop support for installing components into the MTS environment and multiple item selection to make the same changes to more than one object at a time.

  • Operational features such as the ability to lock packages to prevent changes while in deployment, view execution status, and rename packages and security roles.

  • The ability to manage IIS applications running under MTS and packages created by SNA Server 4.0.

The MTS Explorer also includes a Software Development Kit (SDK) for developing applications that install and configure MTS applications automatically.

Microsoft Management Console Support

The MTS Explorer management interface can run as a snap-in to the Microsoft Management Console (MMC). MMC integration provides users with a consolidated management environment with a consistent look. Other software with MMC interfaces includes Windows NT, IIS, and the Microsoft® BackOffice® family.

Automatic Client Installation Utility

While MTS applications require no special libraries or services to run on client machines, some configuration of registry entries must be performed to tell DCOM where to find instances of objects. MTS provides a utility that performs all required configuration actions automatically on clients to let them make calls to MTS-based components transparently.

Focus: Comprehensive Functionality

Automatic Transaction Management

MTS Benefits:
Provides automatic data
integrity protection

Improves database and
application performance

Supports common databases

Simplifies client management

Limits scope of failures

Simplifies data sharing
between components

MTS provides automatic transaction management services to components. Developers or administrators can use simple property page interfaces to specify what level of transaction protection is required by a given component. At run time, MTS performs all required transaction management automatically. This dramatically simplifies development and facilitates component reuse. For more information on MTS transactions, refer to the section titled The Role of MTS Transactions in Components.

Database Connection Pooling

Integrating relational databases within a three-tiered application environment presents a number of challenges:

  • Configuring and maintaining a separate database connection for each client consumes significant database resources and limits scalability.

  • At run time, the cost of establishing a connection to a database can far exceed the cost of the work that the client does in the database.

  • The process of intelligently establishing, managing, and closing multiple database connections requires complex programming.

To address these issues, Microsoft Transaction Server provides an ODBC resource dispenser that creates and manages a reusable pool of ODBC database connections automatically. When application components make standard ODBC calls, MTS locates an available connection, uses it, and returns the connection to the pool when the component completes. The result is greater scalability, much simpler programming for component developers, and significantly faster operation in multi-user environments.

Transactional ODBC Driver for Oracle and DB2 Databases

MTS includes an ODBC 3.0-compliant driver for Oracle Version 7.3 and later as well as IBM's DB2. ODBC 3.0 compliance enables developers to build MTS applications that access these databases (on any platform), along with access to other resources such as SQL Server and Microsoft Message Queue Server, in transactional units of work for full data integrity protection. ODBC 3.0 compliance also enables MTS to perform connection pooling of Oracle and DB2 database sessions for improved performance.

Support for Multiple Databases and Resource Managers

Components running under MTS can access multiple databases and other resources such as message queuing systems and mainframe applications from within a single MTS transaction. Access to resources can be from a single component or from multiple components that are called together to form a single business operation. After all of the work in a transaction completes, MTS initiates a two-phase commit protocol transparently with each resource to ensure that all databases and other resources included in a request commit or abort as a unit.

No Client Footprint

Microsoft Transaction Server does not require any special software or libraries on client platforms. Browser-based clients can access MTS components via IIS, and clients running Windows 95, Windows 98 or Windows NT 4.0 Workstation invoke components transparently through DCOM.

Performance and Scalability Features

Windows NT Workstation and Windows 95/98 Support

MTS supports operation on Microsoft® Windows NT® Workstation, Microsoft® Windows 95, and Microsoft® Windows® 98 to enable companies to deploy stand-alone versions of their MTS applications for development and testing purposes. The same applications can be deployed on Windows NT Server without any modification.

Process Isolation

In many run-time environments, failures within one thread or process can cause failures of entire applications and affect many users. In contrast, MTS Administrators can configure packages to run in their own Windows NT process to ensure that failures in any given package do not spread to other packages.

Automatic Thread Pooling

One way that developers can gain more scalability from their applications is to use Windows NT threads instead of processes for application execution. To make it easy for developers to use threads, MTS provides an automatic thread pooling mechanism. When a request for component execution comes in to MTS from a client, MTS automatically locates an available thread from a pool, executes the component on the thread, and returns the thread to the pool. This simplifies programming and reduces the overhead of thread creation and deletion to improve performance.

Automatic Object Instance Management

In most distributed object environments, instances of server-based objects remain active, and consume server resources, as long as a reference to the object is held by one or more clients. In multi-user environments, this can result in tremendous server resource requirements. To address this issue, MTS extends the COM object model with just-in-time (JIT) activation where components only consume server resources while they are actually executing.

After an MTS-based component indicates that it has completed its work, MTS deactivates the component and recycles its resources. As long as the component is deactivated, only limited server resources remain allocated to it. When the component is called again, MTS reactivates it by reacquiring any resources that the component needs. Most important, from the client’s perspective, only a single instance of the component exists from the time the client creates it to the time it is finally released. No special programming is required.

Shared Property Manager

Many applications require the ability to share data between instances of components. In multi-user scenarios, sharing data safely requires developers to implement locking schemes to prevent concurrent access by multiple components. To make data sharing easier, MTS includes a special-purpose resource dispenser called the Shared Property Manager. The Shared Property Manager enables multiple components to access the same data concurrently—and safely—without complex programming.

Focus: Platform Integration

Integration with Microsoft SQL Server 6.5

MTS Benefits:
Enables components to access SQL Server 6.5 quickly and efficiently.

Makes it easy to develop powerful Web-based applications.

Simplifies asynchronous communication between
components.

Simplifies access to mainframe applications and data.

Supports fault-tolerant
configuration.

Simplifies security policy
development and enforcement.

Microsoft SQL Server 6.5 provides an OBDC 3.0 driver that provides extremely fast and efficient transactional integration, based on the OLE Transactions protocol and the Microsoft Distributed Transaction Coordinator (DTC), to MTS applications. OLE Transactions provides fast, efficient transaction coordination interfaces. Because MTS uses DTC for transaction management, administrators can use the same utilities to manage transactions between MTS and SQL Server, simplifying systems administration.

IIS Integration

Microsoft Internet Information Server version. 4.0 is integrated with MTS and uses MTS for many run-time services such as transaction management. MTS integration makes it easy for IIS Active Server Pages to call MTS components that access databases, mainframe applications, and message queues with full data integrity protection. MTS integration also provides IIS with process isolation to prevent individual failures from affecting other parts of a Web site, with enhanced run-time services such as thread and connection pooling for better performance, and with easier component management.

MSMQ Integration

MTS Application developers can include MSMQ operations (i.e., send and receive) within transactional units of work that contain other activities such as database updates. MSMQ operations commit or abort along with other resources in the transaction to preserve data integrity. For example, if an application updates a database and sends a message to another application within a transaction, any abort condition will cause the database updates to roll back. MSMQ will also roll back by canceling the send operation. When used within a transaction, MSMQ never completes send operations until transactions commit. This prevents receivers from getting messages from transactions that aborted.

MSMQ takes similar actions when receive operations occur within a transaction. If the transaction aborts, MSMQ rolls back the receive operation by putting the received message back in its queue. This message becomes available for receipt by subsequent transactions. The transactional features of MSMQ are important, because many types of problems will occur if abort logic does not include queue operations. For example, if a receive operation does not roll back when associated database operations are rolled back, the message effectively becomes lost, because no database processing occurs.

SNA Server 4.0 Integration

Microsoft SNA Server 4.0 makes it easy to generate COM-based interfaces to mainframe applications for simplified MTS programming. Developers use the graphical tools provided by SNA Server’s Transaction Integrator to designate the COBOL-based communication area definitions in mainframe applications. Then, Transaction Integrator creates COM objects containing the appropriate methods and input/output parameters automatically.

When MTS applications call the components, SNA Server automatically calls the appropriate mainframe applications (for example, running under CICS), which join the current transaction managed by MTS. MTS-based applications can update one or more databases, send or receive messages with MSMQ, and invoke a mainframe application that updates data (for example, in VSAM), and all updates, including those on the mainframe, will commit or abort as a whole.

Support for Windows NT Clustering Services

MTS supports operation on clustered Windows NT Servers. Clustering lets administrators configure MTS for automatic failover and fault-tolerant, high-availability operation. For example, MTS packages can be installed on both machines in a Windows NT cluster environment. When a failure occurs in the online machine, requests for components will be directed to the standby machine once the Microsoft Cluster Service automatically transfers control.

Windows NT Security Integration

MTS provides a distributed security implementation for component-based applications. MTS uses Windows NT security to authenticate users, but provides its own options for authorization. Transaction Server provides two complementary models called declarative and programmatic security. Declarative security is automatic and is specified when components are added into a package and doesn’t require developers to do any programming. Administrators declare which users and groups of users have access to the package using the MTS Explorer. This enables deployment-specific security, even for when used with prebuilt components purchased from third parties.

In contrast, programmatic security lets developers build custom access controls directly into their component by explicitly using roles. For example, roles in a banking application could be called teller and manager, and components could check to make sure that the current user has been given manager privileges before performing certain operations. Administrators associate users with roles using the MTS Explorer. Most important, components themselves have no embedded knowledge of specific users and don’t have to make explicit calls to the Windows NT Security environment. This dramatically improves the ability of a component to be both secure and reusable in a broad sense.

The Role of MTS Transactions in Components

MTS Benefits:

Transactions and Distributed Components

  • Builds transactional integrity into every application.

  • Enables safe integration of components from multiple
    developers.

  • Enables distributed applications across multiple servers and databases.

  • MTS transactions can be virtually transparent to developers.

Transactions are an essential tool for building server-centric applications from component software. To date, most applications are developed as a monolithic application by a single team of developers where it is relatively easy to design transactions properly. But, as companies turn to component-based development, and use prebuilt components developed by other companies, transaction protection becomes much harder to ensure.

A Simple Example

Consider a simple bank funds transfer application assembled from three components —Transfer, Credit, and Debit—each built by a different developer.

  • Credit adds an amount to a checking account database.

  • Debit subtracts an amount from a savings account database.

  • Transfer moves money from checking to savings by calling Credit and Debit.

Assume that Transfer begins work by calling Debit and Credit. Debit succeeds and subtracts $100 from a database. Credit fails because its database was unable to commit successfully. Unless the developer of Transfer has programmed what to do if one of the components has failed, the application will withdraw $100 from the savings account without putting it in a corresponding checking account.

Although this scenario may be easy to correct in a three-component application, typical business applications involving hundreds of components running across multiple servers are unmanageable without transactions. With component-based development, an automatic distributed transaction infrastructure is the only way to address these issues in a cost-effective manner.

Components without Transactions

Transactions in MTS

Components with MTS Transactions

MTS makes transaction management transparent to the component developer. Developers do not have to write begin or end transaction statements into their application code. Developers can even use different tools and languages to build their components. They simply declare components to be transactional using the Transaction Server Explorer. Then, when a component begins execution, MTS starts a transaction automatically if one is required.

When the component accesses a resource such as a database, message queue, or mainframe application, MTS automatically enlists the resource in the transaction. If the component calls another component, the called component also joins the transaction automatically. When all components in a transaction complete their work, MTS initiates a full two-phase commit to either commit or abort the work.

Competitive Differentiation

In many ways, MTS defines an entirely new category of product. MTS uniquely provides a combination of:

  • A component-based development model

  • Strong infrastructure services such as transaction and security support

  • Message queuing, mainframe application, and Web server integration

That said, the two closest competitors to MTS are CORBA-compliant Object Request Brokers (ORBs) that support the Object Transaction Service and Sun’s Enterprise JavaBeans strategy.

CORBA-Compliant Object Request Brokers

While the vision of the Object Management Group (OMG) remains exciting to most corporations, the CORBA approach suffers from a number of weaknesses:

  • CORBA is not a product. The CORBA specification is the result of committees staffed by many different companies with competitive distributed object technologies. To gain agreement, OMG committees frequently had to write specifications that were vague enough to satisfy members with competitive approaches. The result is that CORBA-compliant products from different vendors are only minimally interoperable. In other cases, specifications exist, but no vendor currently provides a compliant product.

  • Important run-time issues such as how to create and destroy server-based instances of objects, and how to pool resources such as database connections, are not addressed by the CORBA specifications. CORBA-compliant products, as a result, frequently experience significant scalability problems.

  • The CORBA specification does not detail support for important component-oriented features such as managing an object’s security and transaction requirements externally. In practice, this severely limits object reuse.

  • Most vendors of CORBA-compliant Object Request Brokers (ORBs) are small and have limited development resources. Consequently, they have focused on developing core ORB capabilities and not on more complex services such as the Object Transaction Service (OTS), which provides transaction management.

  • To use transactions with ORBs, customers have to purchase third-party OTS implementations that add complexity and overhead. Access to message queuing facilities and mainframe-based applications is usually provided by third parties as well. Most customers end up doing substantial amounts of systems integration.

  • Most popular ORBs offer extremely limited systems management capabilities.

The bottom line is that CORBA-compliant ORBs provide a simple way to build basic distributed object applications, but do not provide the infrastructure required for deployment in a production-quality capacity.

Enterprise JavaBeans

More recently, Sun Microsystems announced an initiative called Enterprise JavaBeans. Like CORBA, Enterprise JavaBeans promises developers that they can build reusable components and run them anywhere on a network. Like CORBA, the Enterprise JavaBeans strategy has significant weaknesses:

  • Enterprise JavaBeans is not a product. It is a specification that is the result of input from many companies with competing interests.

  • Pragmatic issues such as database connection pooling are not addressed directly by the Enterprise JavaBeans specification.

  • The Enterprise JavaBeans specification does not detail support for important component-oriented features such as administrative interfaces for managing an object’s security and transaction requirements externally.

  • Microsoft expects that companies will need to assemble various technologies from different vendors, and do systems integration, to incorporate important features such as transactions and message queuing into their applications.

In fact, until the Enterprise JavaBeans specification becomes more complete, it will be difficult for vendors to build compliant products and for customers to assess the viability of Enterprise JavaBeans in their environment.

Summary

The growth of Internet computing is creating explosive demand for businesses to deploy solutions on servers. While the Internet achieved its initial growth by providing an easy way to publish and share information online, businesses now realize that they can achieve even more by Web-enabling their line-of-business applications for Internet and intranet use. Instead of simply publishing sales brochures and product catalogs, businesses can operate accounting systems and order-entry systems on servers with users accessing the shared business functions from now-ubiquitous browsers.

Companies are also realizing that they are wasting huge amounts of programming effort by developing single-use two-tiered and two-and-a-half–tiered applications instead of three-tiered applications with reusable components that run on servers. By developing applications logic once in component form and running components on a server, a variety of different presentation interfaces can be supported. And, since components can call other components easily, duplicating programming efforts is minimized.

Historically, there has been a problem with this server-centric vision. Lacking tool and infrastructure support, building and deploying server-based components is much harder than building single-use applications. Server-based applications need to be more reliable than desktop applications, because the impact of system failure or data corruption can affect an entire business, not just a single user. This mandates a sophisticated infrastructure that is costly to develop and difficult to maintain. By many estimates, infrastructure development represents 30%–40% of the average server-centric development project. To be viable, server-based applications need to be as easy to deploy and maintain as desktop applications, without requiring developers to have special skills or to build costly, complex infrastructure code.

It was with these goals in mind that Microsoft developed Microsoft Transaction Server and included it with Microsoft Windows NT and Microsoft Windows 95. MTS combines the flexibility and low cost of desktop applications with the mission-critical transaction processing features normally found in high-end mainframe systems. And, because Microsoft Transaction Server is based on Microsoft’s Component Object Model, MTS is accessible to a wide range of developers without expensive retraining.

For More Information

For the latest information on Windows NT Server, visit the Microsoft Windows NT server Web site, http://www.microsoft.com/ntserver as well as the Windows NT Server Forum on the Microsoft Network (GO WORD: MSNTS). For more information on Microsoft Transaction Server, visit the MTS area of the Microsoft COM Web site, http://www.microsoft.com/com/mts-f.htm.


Comparing Microsoft® Transaction Server to Enterprise JavaBeans

White Paper
July 1998

For more information, press only:

Bill Schneider
Waggener Edstrom
(503) 245-0905
bills@wagged.com

Introduction

Microsoft® Transaction Server (MTS) is the transaction service in the Microsoft® Windows NT® operating system. First available in 1996, Microsoft Transaction Server technology is now in use at many organizations. Enterprise JavaBeans is a specification for a Java-based transaction service. Created by a group of companies led by Sun Microsystems Inc., the initial specification for Enterprise JavaBeans was released in spring 1998. Both Microsoft Transaction Server and Enterprise JavaBeans target the creation of component-based, transaction-oriented applications. This paper provides a comparison of these two technologies. More specifically, the paper analyzes the similarities and differences between the ways the two component models handle types of objects, transactions support, managing state, controlling complexity, portability, interoperability, language choice and administration.

A Brief Overview

Put simply, a transaction can be thought of as a set of events that are committed or rolled back as a unit — either all of the events happen, or none of them do. For example, transferring $100 from your savings account to your checking account requires both subtracting the money from your savings account and adding it to your checking account. To achieve a consistent result, either both operations should occur or neither one should. Transaction-oriented applications like this are very common; therefore, many vendors provide software that makes writing transactional applications easier. And because transactional applications often need to handle hundreds or thousands of clients (i.e., users at workstations), services that make it easier to build scalable applications are commonly built into products that support transactions.

Both Microsoft Transaction Server and Enterprise JavaBeans support transactional applications and provide services for scalability. Because the two technologies attempt to solve many of the same problems, they have much in common. The easiest way to illustrate their similarities is to describe the basic architecture and terminology of each.

Figure 1

MTS is based on the Component Object Model (COM), and a MTS application is implemented as one or more components. As shown in Figure 1, these components are managed by the MTS Executive. COM objects that run under the control of MTS are called Microsoft Transaction Server objects. As is usually the case with COM, MTS relies on a class factory object to create specific instances of each COM class. To allow MTS to provide extra services, the Microsoft Transaction Server Executive transparently inserts a wrapper object between each object it manages and that object's client. For example, when a client uses the COM IClassFactory interface to create an instance of a MTS object, that call is actually made on a factory wrapper object implemented by MTS, which in turn passes on the call to the real class factory. Similarly, every method call a client makes on the business methods of a MTS object is first handled by a Microsoft Transaction Server-supplied context wrapper object. The Microsoft Transaction Server Executive also supplies a context object for each MTS object. This context object maintains information specific to the MTS object, such as what transaction it belongs to (if any) and whether it has completed its work. Every context object implements the IObjectContext interface, through which a MTS object can access the services that MTS provides.

The general architecture of Enterprise JavaBeans is very similar to MTS; little more than the terminology is changed. As shown in Figure 2, an Enterprise JavaBeans container acts like the Microsoft Transaction Server Executive, hosting enterprise beans rather than MTS objects. Each bean exposes its own Home interface, analogous to the COM IclassFactory interface, allowing a client to create instances of specific classes. And, like MTS, the Enterprise JavaBeans container provides a wrapper for each bean it manages. An Enterprise JavaBeans Home object wraps the bean's Home interface, while an Enterprise JavaBeans Object wraps the bean's business methods. Also, an Enterprise JavaBeans container provides either the javax.ejb.SessionContext interface or javax.ejb.EntityContext interface, both of which offer services similar to Microsoft Transaction Server's IObjectContext. And although it's not shown in the diagram, an enterprise bean is required to implement one or more additional interfaces that the Enterprise JavaBeans container uses to interact with the bean.

Figure 2

Both MTS and Enterprise JavaBeans rely on a transaction coordinator, a logically separate service responsible for ensuring that all operations in a transaction are successful or that none of them are. In MTS, this function is implemented by the Distributed Transaction Coordinator (DTC), a service included in Windows NT. The Microsoft Transaction Server Executive communicates with the DTC through OLE Transactions, a COM-based interface. The Enterprise JavaBeans specification does not define how a transaction coordinator is implemented or how an Enterprise JavaBeans container communicates with the transaction coordinator. Instead, each vendor is allowed to implement these functions in any way it wishes.

Types of Objects

MTS objects are typically designed to encapsulate some set of business functionality. For example, a MTS object might allow a client to transfer money between two accounts or build and submit an order of some kind. Because MTS objects are written as distinct components, business functions encapsulated into these objects can be combined in arbitrary ways, allowing them to be reused flexibly. Although it's not strictly required, a MTS object is typically accessed by one client at a time, and it may (but isn't required to) use transactions.

The Enterprise JavaBeans specification takes a more complex view of objects. Unlike MTS, Enterprise JavaBeans explicitly defines two different types of enterprise beans. The first, called session beans, are much like MTS objects. Session beans encapsulate some segment of business logic, are typically accessed by one client at a time, and optionally use transactions. The Enterprise JavaBeans specification also defines a type of persistent object called an entity bean. Unlike session beans, entity beans represent a specific set of persistent data stored in a database, such as an employee record or a particular order. Each entity bean has a unique key that identifies it, and each one is intended to be accessible by many clients simultaneously. Furthermore, entity beans are required to use transactions.

Building MTS as an extension of COM has other benefits as well. For example, a MTS client need do nothing special to create and work with transactional objects; to a client, a MTS object looks like an ordinary COM object. An Enterprise JavaBeans client, on the other hand, must understand and correctly use new interfaces and methods to create, manage and destroy enterprise beans. Because MTS is built on COM, it automatically benefits from the richness and maturity of the COM environment. Enterprise JavaBeans, by contrast, is forced to invent much of its own infrastructure, including such basics as persistent object support and interfaces, allowing clients to create remote beans.

Supporting Transactions

A transaction defines a set of events that are guaranteed to be committed or rolled back as a unit. Accordingly, every transaction has a beginning, some number of events that are part of the transaction (such as database updates), and an end. Exactly how a transaction's boundaries are demarcated can vary. A common approach in traditional client/server transaction products is to require the client to make a specific call to the transaction coordinator to begin the transaction. The client then makes calls to the components that carry out this transaction's work. The transaction is completed with another explicit call to the transaction coordinator. This final, transaction-ending call can indicate that the transaction should be committed, making all its changes permanent, or aborted, causing all its changes to be rolled back. It's the job of the transaction processing system to ensure that exactly one of these two outcomes occurs.

Transactions in Microsoft Transaction Server

When combining transactions with components, the traditional client-controlled model isn't usually the best approach. With MTS services, Microsoft introduced a new way to demarcate transaction boundaries, called automatic transactions, which allows clients to remain unaware of when transactions begin and end - clients never need to make explicit calls to begin or end a transaction. Instead, the beginning of a transaction depends on the value of that component's transaction attribute. This value can be set to one of four possibilities:

  • If a component is marked as Requires New, the Microsoft Transaction Server Executive will always begin a transaction when its caller first invokes a method in one of that component's objects.

  • If a component is marked as Required, the Microsoft Transaction Server Executive will begin a transaction when its caller first invokes a method in one of that component's objects unless the caller is already part of a transaction. In this case, any methods invoked in the object will become part of the existing transaction.

  • If a component is marked as Supported, the Microsoft Transaction Server Executive will never begin a transaction when a caller invokes methods in one of the component's objects. If the caller is already part of a transaction, the work this object does will become part of that transaction. If the caller is not part of a transaction, this object's work will execute without being wrapped inside a transaction.

  • If a component is marked as Not Supported, the Microsoft Transaction Server Executive will never begin a transaction when a caller invokes methods in one of the component's objects. Even if the caller is part of an existing transaction, this object's work will not become part of that transaction.

When it has finished its work, a MTS object can call either SetComplete, if it wishes to commit that work, or SetAbort, if the work it has done should be rolled back. If neither is called by the time this object's transaction ends, the Microsoft Transaction Server Executive behaves as though the object called SetComplete (the default behavior is to commit the transaction). These calls are made by a method in the MTS object itself, not by the client (again, the client need not be aware that transactions are being used). If every object participating in the transaction agrees to commit, the Microsoft Transaction Server Executive will tell the transaction coordinator to commit the transaction. If any MTS object in this transaction calls SetAbort, however, the Microsoft Transaction Server Executive will tell the transaction coordinator to abort the transaction.

Transactions in Enterprise JavaBeans

The Enterprise JavaBeans specification defines three approaches to demarcating transaction boundaries:

  • Client-managed transactions, in which a client makes explicit calls to begin and end each transaction

  • Container-managed transactions, which allow a client to rely on the Enterprise JavaBeans container to begin and end transactions automatically

  • Bean-managed transactions, in which an enterprise bean makes explicit calls to begin and end transactions

Given that its creators are trying to ensure that Enterprise JavaBeans can be retrofitted onto existing legacy products, they have chosen to support the traditional client-managed approach to transaction demarcation. Enterprise JavaBeans has also adopted MTS's innovation of automatic transactions. In fact, Enterprise JavaBeans's container-managed transactions are nearly an exact clone of MTS's automatic transactions. An enterprise bean can be labeled as TX_REQUIRES_NEW, TX_REQUIRED, TX_SUPPORTS or TX_NOT_SUPPORTED, all of which have the same meaning as in MTS. And similar to MTS, Enterprise JavaBeans assumes that a bean wishes to commit the transaction unless it explicitly invokes the setRollbackOnly method. Enterprise JavaBeans does not provide an analog to the MTS SetComplete call, however.

At first glance, Enterprise JavaBeans' many choices can appear attractively flexible. But they are also very complex. What happens, for instance, when different approaches to transaction demarcation are used simultaneously? Suppose an application begins a client-managed transaction, then executes a method in an enterprise bean that's configured to require a new container-managed transaction. And suppose this bean creates yet another bean that's using bean-managed transactions. What happens when the client commits its transaction? Understanding all the possible combinations (legal and illegal) and their outcomes will be very challenging for developers. And if developers can't easily understand the outcome of their transactions, it will be difficult for them to write correct applications? Compared to the simple, unified model that MTS provides, Enterprise JavaBeans offers developers a confusing and potentially error-prone array of choices for controlling transaction boundaries.

MTS provides all three transaction demarcation options found in Enterprise JavaBeans, but in a less confusing way. As previously described, Enterprise JavaBeans container-managed transactions are virtually identical to MTS automatic transactions. MTS also supports client-managed transactions, allowing a client to determine explicitly when a transaction should begin and end. But unlike Enterprise JavaBeans, MTS builds this service on top of automatic transactions rather than introducing an entirely separate mechanism. This eliminates the possibility of confusion between automatic transactions and client-managed transactions. And, finally, a MTS object with its transaction attribute set to Not Supported can make direct calls on the DTC using OLE Transactions. By doing this, a MTS object can demarcate transactions on its own, just like an Enterprise JavaBeans bean-managed transaction.

Managing State

A typical transactional application accepts input from a client, such as a user at a workstation, then makes changes to one or more databases based on this input. If the transaction commits, the database changes are made permanent. If it aborts, those changes are rolled back. Ensuring the consistency of the databases' state in this kind of application is a primary goal of a transaction processing system.

But what about the state of the application itself? The objects that make up this application may retain information in memory during the transaction. For example, an object that transfers money between two bank accounts may keep the balance of both accounts in memory while the transaction is in progress. If the transaction commits, this in-memory state will match what is in the database. If the transaction aborts, however, this state will be wrong, because neither MTS nor Enterprise JavaBeans will roll back the state of in-memory objects. This potential for hanging on to incorrect data poses a problem for application developers: How can they ensure that their in-memory state is in sync with what's in the database after a transaction has ended? Without some way to achieve this, the application might rely on this incorrect in-memory information to handle the next request, leading to invalid results.

MTS's solution to this problem is simple and direct: When an object calls either SetComplete or SetAbort, indicating that it has completed its work, MTS deactivates that object. Although clients still retain their references to those objects, MTS silently re-creates another instance the next time the client calls a method, a process called just-in-time activation. No MTS object that is participating in a transaction is allowed to maintain any in-memory state across a transaction boundary. Instead, any state that object had is destroyed by the time the transaction ends and so must be refreshed from a reliable source (e.g., the database) when it is next needed. This completely eliminates the possibility of retaining incorrect state that could inadvertently get used in the next transaction. In Enterprise JavaBeans, however, this possibility is quite real. An enterprise bean isn't deactivated when a transaction ends, which means that Enterprise JavaBeans developers face the problem of ensuring that the bean's state matches what's on a disk.

The Enterprise JavaBeans specification places greater burden on the developer, stating that an enterprise bean "must refresh any potentially stale database data it contains at the beginning of the next transaction." This isn't a problem in MTS, since the end of a transaction also marks the end of any state maintained by a MTS object. The result is that it is easier to write correct applications with MTS, since there is no risk of maintaining old, stale data in a MTS object between transactions.

Another benefit of destroying an object's state at the end of each transaction is that it forces the object to release any shared resources it is holding. This greatly improves application scalability, since those resources are now free to be used by another object. Perhaps the best example of a limited shared resource is an open connection to a database. One of the primary reasons that three-tiered distributed applications, such as those built using MTS, can scale better than two-tiered applications is that they can share these database connections. Rather than requiring one for each client, for example, MTS allows a group of open database connections to be stored in a pool and handed out as needed. When a MTS object has finished its work, the object is deactivated and the database connection is returned to the pool for use by another object. This sharing of database connections in MTS is accomplished by the ODBC resource manager, which transparently maintains a pool of open connections. Because each MTS object is forced to give up its connection by the end of each transaction, a single object can't hoard this scarce resource.

Enterprise JavaBeans, however, does not deactivate an enterprise bean at the end of each transaction and so doesn't require the bean to give up its database connections. Enterprise JavaBeans doesn't define any specific mechanism for pooling and reusing database connections. An Enterprise JavaBeans application deployed on Windows NT can use the ODBC resource manager to pool connections just as a MTS application would because this service isn't specifically tied to MTS. But what about Enterprise JavaBeans applications deployed on other systems? Unless those systems have some way to efficiently share database connections, one of the primary benefits of a three-tiered architecture is lost. The Enterprise JavaBeans specification is silent about how this should be done.

Controlling Complexity

A primary goal of any transaction processing platform should be to control the complexity inherent in writing transactional applications. Understanding difficult business problems, then creating the correct code to solve them is hard enough without the need to spend time wrestling with a complex environment. Enterprise JavaBeans has difficulty meeting the goal of simplicity. Instead, Enterprise JavaBeans looks like a combination of everyone's ideas about how to accomplish transaction processing.

For example, as previously described, Enterprise JavaBeans transaction demarcation is very complex, with three different options spelled out in the specification and a myriad of possible ways to combine those options. Enterprise JavaBeans also defines two types of enterprise beans: session beans, representing business logic, and entity beans, intended to represent persistent data stored in a database. These two bean types must be treated quite differently by developers. Support for entity beans is not required by the current version of the Enterprise JavaBeans specification, so an Enterprise JavaBeans-compliant vendor can implement only session beans if desired. This adds another variable to Enterprise JavaBeans' already-complex set of options. A session bean, for example, must be configured to be either stateful or stateless, depending on the choices its creator makes. Entity beans can have either bean-managed persistence, with the bean itself responsible for issuing database calls to load and save its persistent data, or container-managed persistence, where the Enterprise JavaBeans container does this on the bean's behalf. And entity beans can be labeled as either re-entrant or nonre-entrant, depending on whether the developer has made provisions for bean re-entrancy. Furthermore, because entity beans are designed to be accessible to many clients at once, there must be some way to control concurrent access to their data. The Enterprise JavaBeans specification gives two illustrative examples of how this might be done but leaves the door open for any other approach.

Furthermore, both session and entity beans can be accessed with either Java's native Remote Method Invocation (RMI) protocol or through the Internet Inter-ORB Protocol (IIOP) defined by the Object Management Group (OMG). The OMG has also discussed the possibility of combining these two, which could eventually add yet a third protocol option. The truth is that even a casual reading of the Enterprise JavaBeans specification makes clear that this is a technology created by committee. The authors of Enterprise JavaBeans suffer from the difficulty of pleasing a diverse group of vendors, many of whom plan to retrofit existing transaction products to support this new technology. Accordingly, Enterprise JavaBeans attempts to support products that are both component-based and not component-based, database-oriented and not database-oriented, and more. This results in significant complexity and a diffuse approach to the problem.

Compare this complexity with the approach taken by MTS technology. MTS defines one style of component - a COM object - and one powerful but simple model for defining transaction boundaries. MTS components need not be configured as stateful or stateless, and they're always accessed in the same way: through COM calls. Remote clients that speak Distributed COM (DCOM) can directly invoke methods in MTS objects on other machines. For access from Internet-enabled clients via HTTP, the Microsoft Transaction Server Executive is loaded into Internet Information Server (IIS), the Web server built into Windows NT. An IIS application can then make COM calls into the same MTS objects. The creators of MTS understood the importance of simplicity, and its easy-to-understand programming model is one of MTS's most attractive characteristics.

Portability

Portability of enterprise beans across implementations from different vendors is frequently cited as a primary goal of Enterprise JavaBeans. As stated in the Enterprise JavaBeans specification, "An enterprise bean can be developed once and then deployed on multiple platforms without recompilation or source code modification." Yet the specification also states that "specialized containers can provide additional services beyond those defined by the Enterprise JavaBeans specification. An enterprise bean that depends on such a service must be deployed only in a container that supports the service." In practice, this is all but certain to lead to significant differences among products supporting Enterprise JavaBeans, which brings into question its validity as a standard.

This approach to standardization is reminiscent of the OMG's Common Object Request Broker Architecture (CORBA), where vendors introduced significant differences in their products to gain proprietary advantage. Even though the Enterprise JavaBeans specification repeats the familiar claim that enterprise beans will be "Write Once, Run Anywhere," this cannot be true in a world of specialized, proprietary Enterprise JavaBeans containers. While the specification does define a common core that every product must support, the history of ensuring portability based on nothing but paper specifications isn't encouraging (CORBA is only the most recent failed attempt at achieving this). The bottom line is that true portability of enterprise beans is likely to be elusive.

Interoperability

An important aspect of an effective distributed environment is seamless, secure communication among machines. MTS accomplishes this by defining one way to access MTS objects - COM calls - and specifying standard solutions for security and directory protocols. It is less clear how this will be accomplished with Enterprise JavaBeans.

As already noted, the Enterprise JavaBeans specification allows either the native RMI protocol or IIOP to be used for communication, with perhaps a third choice on the way. RMIs native protocol, now known as Java Remote Method Protocol (JRMP), is a very simple technology. It makes no provision, for example, for carrying a client's identity to the server, nor does it define a standard way to convey information about any transaction that might be in progress. The result is that even though RMIs JRMP appears to be a core protocol for Enterprise JavaBeans, this protocol cannot reasonably be used for interoperability among different vendors. Instead, using JRMP for communication from a client to an Enterprise JavaBeans server, or from one Enterprise JavaBeans server to another, requires either going without fundamental Enterprise JavaBeans functions, such as security and distributed transaction support, or adding proprietary extensions.

For multivendor communication, the Enterprise JavaBeans specification assumes that IIOP will be used. Yet using IIOP to access enterprise beans raises several questions. First, there are well-known technical issues still outstanding with this protocol, such as how servers learn of client's untimely deaths. Equally important, the creators of Enterprise JavaBeans seem to place a great deal of faith in IIOP, stating in the Enterprise JavaBeans specification that Enterprise JavaBeans's CORBA mapping "enables on-the-wire interoperability among multiple vendors' implementations of an Enterprise JavaBeans server." Given that products implementing CORBA's Object Transaction Service (OTS) over IIOP don't interoperate today even among themselves, it is difficult to understand how they will interoperate with a completely foreign technology such as Enterprise JavaBeans? And the Enterprise JavaBeans specification doesn't require using the CORBA OTS to communicate with a transaction coordinator. This means that even if IIOP interoperates flawlessly among different vendors' products, Enterprise JavaBeans-compliant products may still not work together because they use incompatible transaction coordinators. Promises of interoperability are easy to make as long as there are no actual Enterprise JavaBeans products. Once there is shipping code, however, customers are likely to find themselves with less than what they've been promised.

In fact, Enterprise JavaBeans' attempt to merge the Java environment with CORBA is inherently problematic. The Enterprise JavaBeans specification is owned by Sun, a company that has largely exited the CORBA business. Many of the Enterprise JavaBeans partners have not, however. The perceived need to merge the Java world with CORBA stems as much from the political needs of the vendors involved as from real user requirements. And, as was true with CORBA, the creators of Enterprise JavaBeans clearly want to allow differentiation among their "standard" products. Enterprise JavaBeans vendors appear to want to have it both ways: They wish to say they have a standard but still retain substantial proprietary elements in their products.

Furthermore, JRMP and IIOP provide only basic communication services and are not suited for this type of functionality. What about protocols for distributed security and directory services? Both the Java and CORBA camps define interfaces for these services, but interfaces alone are not sufficient to ensure interoperability; well-defined wire protocols are required. But agreements on what security and directory protocols should be used with Enterprise JavaBeans (or with CORBA) have yet to appear. Contrast this with MTS: By using standard protocols in a consistent way, MTS makes it easy to build an effective distributed environment, one that's capable of supporting the kinds of mission-critical uses targeted by transaction-oriented applications.

Standardization by Specification

Writing specifications is a useful exercise, but they are not put to the test until implementation. To define a standard and write and publicize the specification but only then build it and see if it works is not a good idea, as has been shown by many failed standardization efforts. In fact, today's most successful standardization organization, the Internet Engineering Task Force (IETF), requires working implementations before finalizing a standard.

Yet it is doubtful that anybody actually built a complete implementation of Enterprise JavaBeans before the final specification was released. When this specification actually is implemented, the first Enterprise JavaBeans developers will probably find things that don't work or can't be effectively mapped to existing products. The result will be either changes to or more loosening of the specification.

And as might be expected from a specification-only technology, Enterprise JavaBeans doesn't define every detail. It's hard to imagine that multiple vendors can build interoperable implementations or that third parties can create a market for truly portable components with only the Enterprise JavaBeans specification to guide them. Time after time, paper-only standards have proved to be an ineffective means of achieving interoperability and portability. Without a reference implementation, there's just too much room for ambiguity.

MTS, by contrast, is a mature product, available since late 1996 and currently in use at hundreds of customer sites. And because MTS is an actual implementation rather than just a specification, its behavior is by definition unambiguous. Shipping software is much more effective than shipping specifications.

Choices for a Development Language

Choosing a programming language is one of the most important decisions made on any project. Striking the right balance between power, ease of use and long-term support is critical. Because no one language can meet every need, MTS allows building applications in any language that supports COM. This list includes the Microsoft® Visual Basic® development system, C++, Java and COBOL.

By design, however, Enterprise JavaBeans allows development only in Java. To the Java proponents at many vendors, most of whom have roots in C++, Java seems simple. And compared to C++, Java is simple. But the users of transaction processing software are primarily business developers, very few of whom know C++. Instead, their experience is likely to be in COBOL or Visual Basic, languages that are much more common among end users.

Both COBOL and Visual Basic can be used to write MTS applications, but neither can be used with Enterprise JavaBeans. If an organization does wish to use Java, MTS has excellent support for developing Java components. Yet Java is probably too complex for many business developers and, in any case, the entire industry will never settle on a single programming choice. By tying its fate exclusively to one language, Enterprise JavaBeans greatly limits its usefulness. And by making that language Java, Enterprise JavaBeans leaves behind a substantial percentage of its target developer market.

Administering a Distributed Transaction Environment

A component-based transaction system relies heavily on its administrator. For example, both MTS automatic transactions and Enterprise JavaBeans container-managed transactions allow setting a component's transaction requirements administratively. Both technologies also allow a component's security requirements to be set by an administrator. Other important functions, such as how in-doubt transactions should be resolved, must be performed administratively as well.

MTS provides standard graphical tools that allow an administrator to perform these types of functions. But while the Enterprise JavaBeans specification acknowledges the importance of the administrator's role, it does not explain how that role should be carried out. Without standards for administration, every Enterprise JavaBeans product will be different. Setting a component's transaction or security requirements, for example, is likely to require entirely different processes in different vendors' products. In practice, these kinds of differences will make it difficult for an organization to install and effectively use Enterprise JavaBeans containers from different companies. As is true in the CORBA world today, customers will be strongly motivated to choose and stick with a single vendor. To a very large degree, this will obviate the motivation for defining a multivendor standard in the first place. Instead, users who obtain a "standard" Enterprise JavaBeans product will find that changing to another vendor's Enterprise JavaBeans implementation is too painful to contemplate - there's just too much effort required.

Summary

The Enterprise JavaBeans specification attempts to define a platform for building transaction-oriented applications. The reality, however, is that this technology is rife with problems. The most significant are as follows:

  • Enterprise JavaBeans is remarkably complex, with many options.

  • Enterprise JavaBeans is defined by a committee-created specification only; there is no reference implementation to help insure interoperability.

  • Enterprise JavaBeans very likely won't allow true portability of enterprise beans, despite claims to the contrary.

  • Enterprise JavaBeans-based applications will have problems with interoperability because of the looseness and diversity of protocols allowed.

  • Enterprise JavaBeans allows application development in only one language, a language that is probably not suitable for a large part of its intended user base.

  • Enterprise JavaBeans leaves important parts of the problem undefined, such as how administration is accomplished, allowing vendors to create differentiation in what are touted as standard products.

MTS services suffer from none of these problems. Its simple programming model makes it easy to create components in any popular language, and those components will work in any MTS environment. MTS is also much more mature, a critical attribute when choosing a foundation for mission-critical transactional applications. And, most important, MTS is available today as a part of Windows NT. Given all these advantages, it is clear that Microsoft Transaction Server is the right choice for building enterprise applications.

##########

Microsoft, Windows NT and Visual Basic are either registered trademarks or trademarks of Microsoft Corp. in the United States and/or other countries.

Other product and company names herein may be trademarks of their respective owners.

The information contained in this document represents the current view of Microsoft Corp. on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.

This document is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS DOCUMENT.


Writing Microsoft Transaction Server Components in Java

Paul Stafford
Developer Relations Group
Microsoft Corporation

Building applications from software components. Writing components in Java. Using transactions to ensure robustness and scalability. To many developers, all of these are sounding like good ideas.

How about using transactions and Java components to build robust, scalable server applications? When Microsoft® Transaction Server (MTS) 1.0 shipped in December of 1996, it became the first product to provide transactions for Java.

This article is about developing MTS application components in Java. After an overview of the process, I’ll build a simple component, step by step. Next I’ll show two client applications, and run the component under MTS. I’ll finish with some debugging tips and pointers to further information on MTS and Java.

To build and run the samples, you will need both Microsoft Transaction Server and Microsoft® Visual J++®. If you don’t yet have MTS, you can order a free evaluation copy from www.microsoft.com/transaction.

I’ll assume you know the basics of the Component Object Model (COM), the Java programming language, and MTS programming. If you are new to MTS programming, a good place to start is the Programmer’s Guide section of the MTS Reference. Reading Part I (Overview and Concepts) and stepping through the tutorial in Part II (Developing Applications in Microsoft Transaction Server) will provide more than enough background for this article.

The MTS Development Process for Java

1. Create the IDL for your component.

When building MTS components in Java, the first step is to provide definitions for your COM-creatable classes (“coclasses”) and for the interfaces that they implement. You do this by writing an IDL (Interface Description Language) file.

Your interfaces must only use COM types that can be mapped to Java types. Using OLE automation compatible types and dual interfaces is a good choice. All of the these types map to Java types, and a dual interface allows your component to be called from both early-binding (vtable) and late-binding (IDispatch) clients. This makes your Java server objects fully accessible from Microsoft® Visual Basic® clients and various scripting languages such as Microsoft® Visual Basic® Scripting Edition (VBScript), JavaScript, Active Server Pages, etc).

Additionally, if you plan on using your component from VBScript, all out parameters must be of type Variant.

For a complete table of the mapping between COM types and Java types, see The Microsoft® Visual J++® documentation under “Type Mappings Between Java and COM”.

2. Generate GUIDs.

Each interface, class, and library in an IDL file needs a COM globally unique identifier (GUID). Use the Microsoft GUID creation tool (guidgen.exe) to generate new GUIDs. You can run GUIDGEN from the command prompt, or by choosing “Create GUID” from the Tools menu in Microsoft® Developer Studio®.

3. Generate the type library file.

The next step is to compile your IDL specification into a type library (.tlb) file. Do this by running the Microsoft IDL Compiler (midl.exe) against your .idl file. To build MTS components, you will need version 3.01.59 or newer of MIDL (version 3.01.59 ships with MTS 1.0). Earlier versions did not correctly handle IDL files with multiple custom attributes. You will need to specify multiple custom attributes when building MTS components in Java.

4. Generate Java wrapper classes for your coclasses and interfaces.

Next, create Java wrappers for the COM coclasses and COM interfaces described in the type library. Do this by running the Visual J++ Java Typelib Conversion Utility (javatlb.exe) against the .tlb file. JAVATLB will create one wrapper class (.class file) for each coclass in the type library. It will also create a wrapper class for each interface.

5. Generate type library summary information.

Generate type library summary information by running JAVATLB with the /U:T option. This will create a file called summary.txt, which will provide Java declarations for each class and interface in the type library, and Java signatures for each interface method. The method signatures make handy templates when you begin writing Java classes that implement your interfaces.

6. Run JAVAGUID against the wrapper classes.

This step is necessary because, as stated in the MTS 1.0 Readme, “whenever any of the MTS API’s need a parameter which is a GUID, you must pass an instance of class com.ms.com._Guid. Do not use Guid, CLSID or IID from package com.ms.com; they will not work.” MTS includes a utility, JAVAGUID, which post-processes the .class files output by JAVATLB, making the necessary changes.

7. Implement your classes in Java.

For each coclass defined in your IDL, create a Java class implemention. The name of this class must match the name specified in the JAVACLASS custom IDL attribute. The class must implement all of the coclass’s interfaces. Again, using the templates from summary.txt is an easy way to get started.

8. Compile your Java classes.

Next, compile your Java implementations. Do this by running the Microsoft Visual J++ Compiler (jvc.exe) against each Java source file; JVC will produce corresponding Java class files.

9. Build your COM DLL.

The next step is to take the type library, COM wrapper classes, and Java classes that you have generated so far, and gather them into a COM DLL. Do this by using the Java Executable Generator (exegen.exe). You’ll need the updated version of EXEGEN that comes with MTS – this version supports a new /D switch for creating DLLs.

10. Deploy your DLL into Microsoft Transaction Server.

Use the MTS Explorer to create an empty package, then drag your file from Microsoft® Windows® Explorer and drop it into that package.

Sample program: Hellojtx

In this section I’ll step through the process of writing a “hello world” Microsoft Transaction Server sample in Java. The sample is called Hellojtx (“hello world in Java, with transactions”).

To build Hellojtx, you will need the EXEGEN, MIDL, and JAVAGUID tools that come with Microsoft Transaction Server. You’ll also need the JAVATLB, JVC, and GUIDGEN tools that come with Microsoft Visual J++.

In the examples below, I am using c:\src\hellojtc as the root directory for my Hellojtx project. I am assuming that all of the command-line tools except EXEGEN are in my path. If your machine is set up differently, you may need to alter the command-line entries and the build file (build.bat).

The Hellojtx IDL

Begin by writing hellojtx.idl:

Listing 1. The Hellojtx IDL.

// hellojtx.idl : IDL source for hellojtx.dll
//
// This file will be processed by the MIDL tool to
// produce the type library (hellojtx.tlb).

#include <MtxAttr.h>
#include <JavaAttr.h>

[
   uuid(CBD73841-C640-11d0-BD14-0080C7C1FC93),
    version(1.0),
   helpstring("Simple Microsoft Transaction Server sample, written in Java")
]
library HELLOJTXLib
{
   importlib("stdole2.tlb");

   [
      object,
      uuid(CBD73842-C640-11d0-BD14-0080C7C1FC93),
      dual,
      helpstring("IHellojtx Interface"),
      pointer_default(unique)
   ]
   interface IHellojtx : IDispatch
   {
      import "oaidl.idl";
      HRESULT SayHello([out] BSTR* pbstrResult, [out, retval] long* plRetVal);
   };

   [
      uuid(CBD73843-C640-11d0-BD14-0080C7C1FC93),
      helpstring("Hello Class"),
      JAVACLASS("Hellojtx.HelloObj"),
      PROGID("Hellojtx.Hello"),
      TRANSACTION_REQUIRED
   ]
   coclass CHellojtx
   {
      [default] interface IHellojtx;
   };
};

The Hellojtx server will implement one COM-creatable class:

   [
      uuid(CBD73843-C640-11d0-BD14-0080C7C1FC93),
      helpstring("Hello Class"),
      JAVACLASS("Hellojtx.HelloObj"),
      PROGID("Hellojtx.Hello"),
      TRANSACTION_REQUIRED
   ]
   coclass CHellojtx
   {
      [default] interface IHellojtx;
   };

The very long number is the globally unique COM identifier (GUID) for this class. Clients written in C or C++ will ask for the class by this GUID. Other clients prefer shorter, more readable names. Visual Basic clients will ask for the Programmatic Identifier (ProgID) specified in the PROGID custom IDL attribute (Hellojtx.Hello). Most humans also prefer ProgIDs, so I will call this class Hellojtx.Hello.

When JAVATLB creates a Java wrapper for this class, it will get the name of the wrapper class from the IDL coclass entry: CHellojtx.class.

When we write a Java implementation for Hellojtx.Hello, we must match the JAVACLASS attribute. We’ll name our Java class HelloObj.class and place it in the Hellojtx package.

When we use the MTS Explorer to deploy our component, it will see the TRANSACTION_REQUIRED attribute and mark our component as needing a transaction. Alternatives to TRANSACTION_REQUIRED are TRANSACTION_SUPPORTED, TRANSACTION_NOT_SUPPORTED and TRANSACTION_REQUIRES_NEW. This example does not in fact need to use transactions, but was marked as requiring one to show you how it’s done.

Hellojtx.Hello components implement one interface, IHellojtx:

   [
      object,
      uuid(CBD73842-C640-11d0-BD14-0080C7C1FC93),
      dual,
      helpstring("IHellojtx Interface"),
      pointer_default(unique)
   ]
   interface IHellojtx : IDispatch
   {
      import "oaidl.idl";
      HRESULT SayHello([out] BSTR* pbstrResult, [out, retval] long* plRetVal);
   };

The IHellojtx interface has one method, SayHello (the methods it inherits from IDispatch are not shown in the IDL). The types specified in SayHello’s signature are all COM types; JAVATLB will map these types to native Java types.

Hellojtx’s GUIDs

I used GUIDGEN to create fresh GUIDs for the HELLOJTXLib library, the IHellojtx interface, and the CHellojtx coclass. For your own projects, remember to create new GUIDs!

Run MIDL

Run midl.exe against Hellojtx.idl:

C:\src\hellojtx>MIDL hellojtx.idl

This will create the type library file hellojtx.tlb. You can use the OLE Object Viewer application (oleview.exe) to examine the contents of the type library.

Run JAVATLB

Run javatlb.exe against hellojtx.tlb:

C:\src\hellojtx>JAVATLB /d . /p Hellojtx /p:b- hellojtx.tlb

The /d . option tells JAVATLB to use the current directory as the root directory for class file output. Keeping the files under the current directory makes them a little easier to work with for the rest of the build steps.

The /p option specifies Hellojtx as the Java package for the new classes.

JAVATLB creates two new Java class files, one for the coclass and one for the interface. Java requires that file names match class names, so JAVATLB names the files CHellojtx.class and IHellojtx.class. Java also requires that directory names match package names, so JAVATLB puts the files into the Hellojtx subdirectory.

CHellojtx.class is a COM class wrapper. Java clients will use CHellojtx to create instances of the Hellojtx.Hello COM class. For example:

   // code in Java client
   IHellojtx hello = (IHellojtx) new CHellojtx();

When the Microsoft virtual machine loads CHellojtx.class, it will recognize that it is a wrapper and invoke the COM creation API CoCreateInstance to instantiate an instance of the Hellojtx.Hello COM class.

IHellojtx.class is a COM interface wrapper. At runtime, the Microsoft VM will use this class to map incoming calls from COM clients to method calls on the Java class Hellojtx.HelloObj.

Generate summary.txt

This step is optional. Create type library summary information for Hellojtx by running JAVATLB with the /U:T option:

C:\src\hellojtx>JAVATLB /U:T hellojtx.tlb
Microsoft (R) Visual J++ Java Typelib Conversion Utility Version 1.01.7048
Copyright (C) Microsoft Corp 1996. All rights reserved.

import hellojtx.*;

This output shows that Java clients can import the Hellojtx package by using the statement import hellojtx.*.

Here is the content of summary.txt:

public class hellojtx/CHellojtx extends java.lang.Object
{
}
public interface hellojtx/IHellojtx extends com.ms.com.IUnknown
{
    public abstract int SayHello(java.lang.String[]);
}

Here we see how JAVATLB maps COM types specified in the IDL into Java types. Recall that in the IDL, we used COM types in SayHello’s signature:

   HRESULT SayHello([out] BSTR* pbstrResult, [out, retval] long* plRetVal);

Since Java only supports input parameters, JAVATLB generates an array of String objects to implement the BSTR out parameter. SayHello’s Java implementation will set the first element of the array to return an output string to its caller. JAVATLB uses this same technique on all [out] parameters specified in IDL.

JAVATLB maps the retval parameter plRetVal into an integer return value.

NOTE: JAVATLB /U:T outputs summary.txt, along with new .class files, to a directory in the Java class path. On my machine, it puts these files in c:\winnt\java\trustlib\hellojtx. To avoid having multiple copies of class files lying around my system, I delete them from this directory and copy summary.txt back to the root directory of my project.

Run JAVAGUID

Post-process the new class files by running javaguid.exe. JAVAGUID can only be run from the command line, and only from the same directory as its target files. Change to the Hellojtx subdirectory, then invoke JAVAGUID:

C:\src\hellojtx>cd Hellojtx
C:\src\hellojtx\Hellojtx>JAVAGUID CHellojtx.class IHellojtx.class
C:\src\hellojtx\Hellojtx>cd ..

JAVAGUID does not create any new files. It edits its target files, finding each reference to a COM GUID and changing it to com.ms.com._Guid. It also adds a static public final member to each class, representing that class’s _Guid. Clients use these members when calling the MTS IObjectContext.CreateInstance method.

Implement HelloObj

Now it’s time to write the Java class that implements the IHellojtx.Hello coclass. The class name and package for this class must match the name previously specified in the JAVACLASS IDL attribute: Hellojtx.HelloObj. Java filenames must match class names, so create a new file HelloObj.class in the Hellojtx subdirectory:

Listing 2. The Hellojtx coclass implementation.

// Simple Microsoft Transaction Server sample application, written in Java
// Copyright (C) 1997 Microsoft Corporation
// All rights reserved.

package Hellojtx;

import com.ms.mtx.*;

public class HelloObj implements IHellojtx
{
   public int SayHello(String[] result)
   {
      try
      {
         result[0] = "Hello from simple MTS Java sample";
         MTx.GetObjectContext().SetComplete();
         return 0;
      }

      catch(Exception e)
      {
         MTx.GetObjectContext().SetAbort();
         return -1;
      }
   }
}

Sticking to tradition, SayHello returns a simple “Hello” message. Notice again how we use an array of Strings to return the out parameter.

Before returning, SayHello calls SetComplete to let MTS know that it is done with it’s work, and that it’s happy – the work it’s just done can be committed:

   MTx.GetObjectContext().SetComplete();

Now, this component does not really need to use a transaction. It’s not doing any work on shared, durable backend data. It’s not calling any Resource Managers. I’m just using a transaction as an example. Using the MTS Explorer’s Transaction Statistics pane, you’ll be able to see this component triggering transactions when it runs under MTS.

Compile CHelloObj.java

Next, compile the CHelloObj implementation:

C:\src\hellojtx>JVC Hellojtx\HelloObj.java

This will produce the file HelloObj.class. This class contains the Java byte codes that will be interpreted by the Microsoft VM at run time. Because HelloObj belongs to the Hellojtx package, HelloObj.class must be in the Hellojtx subdirectory.

Build hellojtx.dll

Now we have all of the pieces that we need: a type library, a wrapper class for the IHellojtx interface, and a Java class that implements the Hellojtx.Hello COM class. It’s time to gather all the pieces into a COM DLL. Run exegen.exe from the command prompt in the root directory:

C:\src\hellojtx>C:\MTX\TOOLS\EXEGEN /d /r /out:hellojtx.dll hellojtx.tlb *.class

The /d switch tells EXEGEN to build a DLL, and the /out switch tells it to name this DLL hellojtx.dll. The /r switch will cause EXEGEN to recurse into the Hellojtx subdirectory, where it will pick up our three class files (CHellojtx.class, IHellojtx.class, and HelloObj.class). EXEGEN will place copies of the class files in the resource fork of hellojtx.dll.

At runtime, hellojtx.dll will be the COM server for the Hellojtx.Hello class. However, it will delegate this task to the Java VM.

The Microsoft VM (msjava.dll) knows how to be an inproc COM server. To implement the Hellojtx.Hello class, the VM will interpret the byte codes in the HelloObj Java class. To map incoming COM method calls to Java method calls, it will use the IHellojtx interface wrapper. It finds both of these classes in hellojtx.dll’s resource fork.

NOTE CHellojtx.class does not actually need to be included in the DLL – at runtime, the Java VM does not need to load the wrappers for COM classes that a DLL implements.

Deploy hellojtx.dll into Microsoft Transaction Server

Fire up the MTS Explorer and create a new package named “hellojtx”. If you’ve never created a new MTS package, just follow the steps listed in the MTS Explorer Help Topics under “Show me how to ... create a new empty package”.

Next, select hellojtx.dll in the Windows Explorer, drag it, and drop it into the empty package. That’s it! The MTS Explorer does all the work of configuring the new component.

Create a build file

It might seem that this process has a lot of steps. However, only two of the steps require writing code: creating the IDL, and implementing the Java classes. Two steps involve simple use of a GUI tool: creating the GUIDs, and deploying the DLL into MTS.

The rest of the steps – running MIDL, JAVATLB, JAVAGUID, JVC, and EXEGEN – can be automated. For example, here is the batch file I used for building Hellojtx:

Listing 3. The Hellojtx batch file (build.bat).

MIDL hellojtx.idl
JAVATLB /d . /p Hellojtx /p:b- hellojtx.tlb

cd Hellojtx
JAVAGUID CHellojtx.class IHellojtx.class
cd ..

JVC Hellojtx\HelloObj.java
C:\MTX\TOOLS\EXEGEN /d /r /out:hellojtx.dll hellojtx.tlb *.class

Clients for Hellojtx

Before we can see the Hellojtx.Hello component run under Microsoft Transaction Server, we need to build some clients. I’ll describe two clients: one written in Java, one in Visual Basic.

The Java Client

The source code for the Java client consists of a single Java file, Client.java:

Listing 4. The Java Client implementation.

import Hellojtx.*;

public class Client
{
   public static void main(String args[])
      {
      IHellojtx myHello;
      String strRet[] = { "<no returned data>" };
      int nRet;

      try
         {
         myHello = (IHellojtx) new CHellojtx();
         System.out.println("Calling SayHello..");
         nRet = myHello.SayHello(strRet);
         System.out.println("nRet = " + nRet);
         System.out.println("strRet = " + strRet[0]);
         }
      catch (Throwable t)
         {
         System.out.println("Exception: " + t.toString());
         t.printStackTrace();
         System.exit(0);
         }
      try
         {
         System.out.println("Hit any key to exit..");
         System.in.read();
         myHello = null;
         System.gc();
         }
      catch (Exception e)
         {
         }
      }
}

Client imports the Hellojtx package:

   import Hellojtx.*;

This allows Client to omit the package name when referring to IHellojtx and CHellojtx.

Now we come to an important point. Here is the correct way for Client to create and call a Hellojtx.Hello component:

      IHellojtx myHello;
      myHello = (IHellojtx) new CHellojtx();
      nRet = myHello.SayHello(strRet);

Client creates a Hellojtx.Hello component by using the Java new keyword, supplying as the argument the name specified as the coclass in the .idl file (CHellojtx). It then casts the result to an interface that the component implements (IHellojtx).

This code works because CHellojtx.class is a COM wrapper. When the Microsoft VM loads this class, it knows to create an instance of the Hellojtx.Hello COM class. The VM handles the cast by issuing a COM QueryInterface call, asking for the IHellojtx interface.

NOTE: Although it might seem intuitive, specifying the object implementation class (in this example, HelloObj) as the argument to new would not work:

   // BAD code – this won’t work.
   HelloObj myHello;
   myHello = new HelloObj();
   nRet = myHello.SayHello(strRet);

This code would cause a runtime exception such as the following:

Exception: java.lang.NullPointerException
java.lang.NullPointerException
        at Hellojtx/HelloObj.SayHello
        at Client.main

When a coworker and I sat down to write our first MTS component in Java, we made this mistake. We were stymied until, in desperation, we read the Microsoft Visual J++ documentation, where this caveat is called out quite clearly.

Notice once again that Client uses an array of strings to obtain SayHello’s out parameter:

      String strRet[] = { "<no returned data>" };
      int nRet;

      <other code>
      nRet = myHello.SayHello(strRet);

In the second try clause, SayHello releases myHello and calls the Java garbage collector:

      myHello = null;
      System.gc();

These calls are optional. If Client omitted them, the Hellojtx component would be left alive until the DCOM connection timed out (by default, six minutes). Making these calls allows the component to shut down immediately.

Building the Java client

Use the Microsoft Java compiler to build the client:

C:\src\hellojtx>jvc Client.java

Assuming it can find the Hellojtx package, JVC will create the file Client.class. If it can’t find the package, it will generate an error such as the following:

Client.java(1,8) : error J0051: Undefined package 'Hellojtx'

In this example, I’m building the client from the root directory of my project. JVC will find the classes it needs (CHellojtx.class and IHellojtx.class) in the Hellojtx subdirectory. If the client were in any other directory, it would not build.

To make the Hellojtx classes globally available, create a package on your machine’s Java class path. On my system, I would create a Hellojtx subdirectory under c:\winnt\java\trustlib, and copy CHellojtx.class and IHellojtx.class into that directory.

Note that clients do not need the HelloObj class. The Microsoft VM needs this class at runtime, and it will find the class in the resource fork of hellojtx.dll.

Running the Java client

Start the Microsoft Transaction Server Explorer. Use the Explorer’s Tools menu to see if the Microsoft Distributed Transaction Coordinator (MSDTC) is already running – if not, start the MSDTC.

Now we are ready to run the Java client, using the Microsoft Command-line Loader for Java (jview.exe). Here is what the result looked like on my system:

C:\src\hellojtx>jview Client
Calling SayHello..
nRet = 0
strRet = Hello from simple MTS Java sample
Hit any key to exit..

If you pause before hitting a key to exit, you can switch over to the MTS Explorer and see the Hellojtx component running. You can also use the Explorer’s Transaction Statistics pane to see that the component is indeed running MSDTC transactions:

Figure 1. Hellojtx.Hello running under Microsoft Transaction Server.

The Visual Basic client

Here is a Microsoft Visual Basic subroutine that calls a Hellojtx component:

Private Sub cmdSayHello_Click()

    Dim myHello As CHellojtx
    Dim str As String
    Dim nRet As Long

    Set myHello = CreateObject("Hellojtx.Hello")
    nRet = myHello.SayHello(str)
    MsgBox str

End Sub

Before building this code, use Visual Basic’s Project References dialog to pull in a reference to Hellojtx’s type library; it will be listed under the helpstring specified in hellojtx.idl (“Simple Microsoft Transaction Server sample, written in Java”).

The only trick here is knowing which of the class’s identifiers to use when declaring the myHello variable and instantiating the object. In Visual Basic, use the coclass name specified in the .idl file (“CHellojtx”) when declaring a variable for the Hellojtx object. Use the ProgID specified in the PROGID custom attribute (“Hellojtx.Hello”) as the argument to CreateObject.

Running the clients remotely

Running the Java and Visual Basic clients remotely is much the same as running them on the same machine as Hellojtx.

Begin by using the Microsoft Transaction Server Explorer to configure Hellojtx.Hello as a Remote Component on the client computer. The Explorer will add all of the necessary registry entries. (If you have not installed an MTS Remote Component before, consult the MTS Explorer Help topic “Configuring Remote Components”.)

Copy the Visual Basic executable (Client.exe) to the client machine. Make sure that MTS is running on both machines, and that the MSDTC is running on the server machine. Now run Client.exe, and it will access Hellojtx across the net.

Copy the Java class (Client.class) to the client machine. When this client runs, the Microsoft VM will need local access to Hellojtx’s COM wrappers, so put copies of Hellojtx’s class files somewhere on the client machine’s Java class path. On my client machine, I copied CHellojtx.class and IHellojtx.class to the c:\winnt\java\trustlib\hellojtx subdirectory.

Now use JVIEW to run the Java client, and it will access Hellojtx across the net.

Debugging

Debugging is one area where writing an MTS component in Java is a little less convenient than writing the component in C++ or Visual Basic. With current development tools, it is not possible to step through the component’s Java source code in the debugger.

One way to get debugging information as a component runs under MTS is to use “sprintf” style debugging. For example, I could add a System.out.println call to the try clause of Hellojtx.HelloObj.SayHello:

      try
      {
         System.out.println("This msg is from the HelloObj implementation");
         result[0] = "Hello from simple MTS Java sample";
         MTx.GetObjectContext().SetComplete();
         return 0;
      }

After rebuilding HelloObj.class and hellojtx.dll, I might expect to see this string in the console window the next time I used JVIEW to load the Client class. However, I would not:

C:\src\hellojtx>jview Client
Calling SayHello..
nRet = 0
strRet = Hello from simple MTS Java sample
Hit any key to exit..

Hellojtx is not running in the same process as JVIEW - it is running in special MTS surrogate process (mtx.exe). This causes HelloObj’s println output to go into the bit bucket instead of into JVIEW’s console window.

The solution is to configure Hellojtx to run in the process of its caller (JVIEW’s process). To do this, use the MTS Explorer. In the Activation pane of Hellojtx’s Properties dialog, configure Hellojtx to run in the client’s process:

Figure 2. Configuring Hellojtx.Hello to run in its client’s process.

Now reload the Client class, and Hellojtx’s println calls will be visible in the output:

C:\src\hellojtx>jview Client
Calling SayHello..
This msg is from the HelloObj implementation
nRet = 0
strRet = Hello from simple MTS Java sample
Hit any key to exit..

A canonical form

The steps for building Hellojtx can serve as a model for building any MTS component written in Java.

The SayHello method, however, is a little too limited to serve as a model. Most MTS component methods are written in much the same form, no matter what programming language is used. Here is how that canonical form looks in Java:

Listing 5. Common form for MTS component methods written in Java.

Public int MTSJavaMethod ()
{
   IObjectContext   context;
   boolean         success = false;
   IFoo            foo = null;

      try
      {
         // obtain MTS context
         context = (IObjectContext) Mtx.GetObjectContext();

         // obtain resources. for example, database connections

         // use resources
         // perform one piece of work for one client

         // invoke other MTS components to do some of the work
         foo = (IFoo) context.CreateInstance(CFoo.clsid, IFoo.iid);

         // all went well
         success = true;
         return 0;
      }

      catch (Exception e)
      {
         return -1;
      }

      finally
      {
         // release resources

         if (success)         // we are finished and happy
            context.SetComplete();
         else            // we are unhappy
            context.SetAbort();
      }
}

Where to go from here

As a next step, you may want to look at the Bank sample that comes with Microsoft Transaction Server. Java source code for the Bank’s MoveMoney and Account classes is installed in the MTX\Samples\Account.VJ\Account directory. The IMoveMoney.Perform and IAccount.Post methods implemented by these classes illustrate more complete implementations of the canonical MTS in Java form shown above.

For complete information on installing and running the Bank package, see the Microsoft Transaction Server Help under “Validating Setup with a SQL Server Transactional Component”.

References

Microsoft Transaction Server Readme. Includes a large section on “Developing Components with Java”. This section contains a great deal of detail, including complete information on the EXEGEN and JAVAGUID tools.

Microsoft Transaction Server Reference. Includes the MTS Programmer’s Guide, documentation for the MTS Java interfaces, and complete instructions for using the MTS Explorer.

Microsoft Transaction Server Evaluation CD. Available from www.microsoft.com/transaction. This CD includes a fully functional (for 120 days) copy of the MTS product. It also includes a great deal of information on programming for Transaction server, including white papers, sample code, Microsoft® PowerPoint® slides, and several hours worth of technical talks on streaming video.

Microsoft Visual J++ documentation. Includes a large section on “Java and COM”, complete details on writing IDL files for Java, and a table of the mappings between COM types and Java types.


Writing MS Transaction Server Components in Java with Visual J++ 6.0

By Larry Hanson
Visual J++ Product Team

September 1998

Introduction Up

This article is about developing Microsoft® Transaction Server (MTS) application components in Java using Microsoft® Visual J++® 6.0. I will show you step by step how to use Visual J++ 6.0 to write and build a server-side Java MTS component that can be called by a Java client application. Next I will show you how to debug the server-side component on your development machine. Finally I will explain how to deploy the server component and the client application to separate machines.

To build and run the samples, you will need both Microsoft Visual J++ 6.0, Microsoft® Windows NT® 4.0 Service Pack 4 and Microsoft Transaction Server.

I'll assume that you know the basics of the Java programming language, Component Object Model (COM), and MTS programming. If you are new to MTS programming, a good place to start is the Programmer's Guide in Microsoft Transaction Server Help. Reading the "MTS Overview and Concepts" and "Developing Applications for MTS" sections will provide more than enough background for this article.

Writing and Building an MTS Java Component Up

The following explains the steps for creating a simple MTS component for Java using Visual J++ 6.0.

Create a COM DLL project.

A Java component must be packaged as a COM DLL to be used by MTS. You can easily create a COM DLL project in Visual J++ by selecting New Project in the File menu then choosing the COM DLL project template under Visual J++, Components in the tree view. Name the project JavaServer and choose Open.

Add MTS aware code to the project's class.

Rename the default Class1.java file to JavaServerClass.java in the Project Explorer and change the contents of the file to the following code:

import com.ms.mtx.*;
public class JavaServerClass
{
public String MTSMethod()
{
// Declare variables, but don't allocate
          resources yet.
String result = null;
boolean success = false;

try
{
// Allocate resources
          and do work in try block
result = "Hello
          from JavaServer!";
success = true;
}
catch(Exception ex)
{
// Handle errors from
          try block
success = false;
}
finally
{
// Clean up resources
          no matter what happened and
// complete or abort
          transaction as appropriate.

if(success)
{
Context.setComplete();
          // No errors. Complete transaction.
}
else
{
Context.setAbort();
          // Error occured. Abort transaction.
}
}
return result;
}
}

Mark the class as COM and MTS enabled.

To use the class in MTS you must mark the class as a COM and MTS class. Visual J++ provides a simple way to mark a class as a COM and MTS class through the Class Outline. Activate the Class Outline by opening JavaServer.java, selecting Other Windows in the View menu and selecting Document Outline. Right click the JavaServerClass in the Class Outline window and select Class Properties. Check the MTS Support Enabled checkbox. Select Supported in the drop down list. You will notice new code appear in JavaServerClass.java marking the class as a COM and MTS class that looks something like the following:

/**
* @com.register ( clsid=A6D25C85-B6DC-11D1-9BAD-00A0C905438F, typelib=A6D25C86-B6DC-11D1-9BAD-00A0C905438F )
* @com.transaction (required)
*/

Build the project.

To build the project simply choose Build under the Build menu. Building the project creates a COM aware DLL file called JavaServer.dll. This DLL contains the JavaServerClass.class and the appropriate stub code so that the DLL can be registered just like any COM DLL written in C++ or Microsoft® Visual Basic®. This DLL file will be deployed to MTS in the next step.

Deploy your DLL into Microsoft Transaction Server.

Start the MTS Explorer and create a new package named JavaServer. If you've never created a new MTS package, see "Creating MTS Packages" in the Microsoft Transaction Server Administrator's Guide in Microsoft Transaction Server Help. Add the new JavaServer.dll component to the JavaServer package you created. Be sure to select Install new components in the MTS Explorer Component Wizard rather than Import components.

When we use the MTS Explorer to deploy our component, it will see the required attribute that Visual J++ marked the class with and recognize our component as needing a transaction. Alternatives to required are supported, notSupported, and requiresNew.

If you make changes to the project and rebuild it after deploying to MTS, you will need to re-register your component in MTS. You can do this by right clicking My Computer in the MTS Explorer and selecting Refresh all components.
Writing and Building a Java Client Up

Now that we have written, built, and deployed an MTS Java component; we need a client that can consume the component. The client can be written in Java, Visual Basic or C++. The following explains the steps for creating a simple Windows Foundation Classes (WFC) based client using Visual J++ 6.0 that can consume the MTS component for Java we created in the previous section. For more information about WFC see the "Introduction to WFC Programming" section in the Visual J++ 6.0 Programmers Guide.

Create a new Windows Application.

You can easily create a Windows Application project in Visual J++ by selecting New Project in the File menu then choosing the Windows Application project template under Visual J++ Applications in the tree view. Name the project JavaClient, be sure that Close current solution is selected and choose Open .

Add COM wrappers for Java MTS component.

Although both the component and the client are written in Java, the client must access the component through COM rather than a direct Java call. Creating and calling the component through COM allows MTS to hook into the creation of the compoonent and provide the right services. An additional benefit of using COM is that it allows us to communicate with this component between machines in the future.

Setup COM access to the Java MTS component by adding COM wrappers to the component. Select Add COM Wrapper in the Project menu. Check the box next to JavaServer in the list of installed COM components in the COM Wrappers dialog and press OK. You will notice a package called javaserver has been added to the JavaClient project.

Add code to call the Java MTS component from the Java client.

Now we need to add code to the application's form to interact with the Java MTS component. First, lets add a label to the form. JavaClient has a default form called Form1. Double click Form1.java in the Project Explorer and Form1 will appear in the Form Designer. Select Toolbox in the View menu to view the toolbox and select the WFC Controls tab. Drag a Label onto the form. It will be named label1 by default.

Double click on the Form1 in the Form Designer and the code for the click event for the form will appear. Add code to import the javaserver COM wrappers by placing the following import statement after the existing import statements in Form1.java:

import javaserver.*;

Add code to call the Java MTS component from the form's click event method by placing code that calls the Java MTS component method and updates the label with the results. The Form1_click method should look something like the following:

private void Form1_click(Object sender, Event e)
{
JavaServerClass_Dispatch server = JavaServerClass_Dispatch)new
          JavaServerClass();
label1.setText(server.MTSMethod());
}

When JavaClient runs and we click on the form, a call will be made to the Java MTS component and a transaction will be started. If the component executes without error, the call to MTSMethod will update the result, call SetComplete() and the transaction will be committed. The string will be returned to the client and lable1 will be updated with the text "Hello from JavaServer".

Build and Run the Client on the local machine.

Select Build under the Build menu to build the project. Building the project creates a Windows executable with all of the class files needed to execute JavaClient, including the COM wrappers for JavaServer, packaged into one executable file.

Run the client.

If the JavaClient project and the JavaServer project are part of the same solution, before you can run the client from the environment you must first mark the JavaClient project as the startup project. Right click on JavaClient in the Project Explorer and select "Set as startup project" in the context menu. To run the client select Start from the Debug menu. Run JavaClient and click on the form. After a short pause, you should see the label updated with the text "Hello from JavaServer". You can view the transaction statistics of the component as it is being called from the client in the Transaction Server Explorer.
Debugging an MTS Java Component Up

With Visual J++ 6.0 you can debug MTS components within the Visual J++ Integrated Development Environment (IDE). To debug your Java MTS component in Visual J++ use these steps to set up debugging. Each of these steps is made either inside the MTS Explorer or inside of a Visual J++ session with your JavaServer COM DLL project.

Shutdown server processes using the MTS Explorer. To do this, right-click My Computer, and select Shutdown Server Process.

In your Visual J++ session, select JavaServer Project Properties from the Project menu and select the Launch tab. Select Custom to activate the custom lunch properties. Set the Program field to the full path of the Mtx.exe process, for example: "c:\winnt\system32\MTx.exe"

In the same property sheet, set the Arguments field to the following string: /p:&rdquo;JavaServer&rdquo;. Be sure that there are no spaces in the argument.

Set breakpoints in the JavaServer component DLL source, and you are ready to debug.

Select Start from the Debug menu to run the process.

Now run JavaClient and the breakpoints you set in step 4 should be hit.

Note Debugging MTS Java components with Visual J++ 6.0 requires Microsoft® Windows NT® 4.0 Service Pack 4 to work correctly.

Setting Up an MTS Java Component on a Server Up

Once you have written and debugged the MTS Java component you will want to setup the the component on a machine other than your development machine such as a staging server or a production server. It is very easy to setup your component on a different machine. All you have to do is copy the Java MTS component DLL (JavaServer.dll in this case) to the other machine and then register the component in the MTS Explorer the same way you did on the development machine. See step 5 of Writing and Building an MTS Java Component above for the steps to register the component in MTS.

Packaging a Java Client for Remote Execution Up

With Visual J++ version 6.0 you can package all of the files required to run and register your client application in a single self-extracting setup executable. The excellent capabilities of Microsoft® Visual Studio® make this very easy.

Create a self-extracting setup project

To create a self-extracting setup project, select New Project from the File menu and then select Self-Extracting Setup under Visual Studio, Distribution Units. Name the project JavaClientSetup and select Open.

Choose the files to be included in the setup.

The setup must contain all the files required to run the client. JavaClient.exe contains most, but not all the files required. In addition, we need to include two important files that allow the client on one machine to communicate with the MTS Java component on the separate server machine. These files are JavaServer.tlb and JavaServer.vbr.

Select Add Item in the Project menu and browse to the JavaClient project directory. Select the file JavaClient.exe and select Open to add it to the setup. Select Add Item in the Project menu, but this time browse to the JavaServer project directory. Select the files JavaServer.tlb and JavaServer.vbr and select Open.

Configure the remote components

Here is where the .tlb and .vbr files that we added to the project in the previous step come into play. The setup has to know how to configure the client machine to communicate with the MTS Java component on the server. The .tlb and .vbr file work together to supply this information to the setup. First the setup must be made aware of the remote component.

Select JavaClientSetup Properties from the Project menu and select the Remote Components tab. JavaServer should be in the list of Remote Components. Check the box next to JavaServer. Doing this activates the remote server machine options. You can choose a remote server machine that the setup will always use by entering the name of the machine in the Use Remote Server text box. Or you can check the Always prompt user for server so that the setup will always ask when setup is run. Press OK or Apply to commit the settings.

Configure the target settings

You can customize various settings of the setup by selecting JavaClientSetup Properties from the Project menu and selecting the Target tab. In this case, change the Application title and Installation folder to JavaClient. Check the Create an entry in the Start menu check box and change the Shortcut name to JavaClient. Finally enter the following string into the Command line Program text box: <appdir>\JavaClient.exe. Now when you run your setup on a machine a Start menu item will allow you to quickly and easily run the client application.

Build the self-extracting setup

To create the setup program select Build in the Build menu. You now have all the files and code required to setup the Java client that access the MTS Java component in a single, self-extracting setup program.

Note Self-extracting setups created with Visual J++ 6.0 Technology Preview 1 do not include redistributable files required for setting up WFC and other important files. Without these files, the client will not execute correctly. The final released version of Visual J++ 6.0 will include these files in the setup. Until then, applications installed with a self-extracting setup will only run on a machine with Visual J++ 6.0 Technology Preview 1 installed.

Signing your setup.

One great feature of the self-extracting setup project is that it automatically signs the setup for you. A test certificate is automatically used by default, but you can customize this setting by selecting the project Properties in the Project menu and selecting the Signing tab.

Deploy and run the client

You can deploy your client setup from a web page, an FTP site, or whatever way is convenient. As a simple test, run JavaClientSetup.exe on a separate machine. When prompted for the remote machine name enter the name of the machine you setup the JavaServer component on. Launch JavaClient from the Start menu entry. When the JavaClient form appears, click on the form. After a short pause, you should see the label updated with the text "Hello from JavaServer" except this time the data displayed is coming from a different machine!
Next Steps Up

You can see from this example that Visual J++ 6.0 makes developing, debugging and deploying Microsoft Transaction Server based Java solutions very easy. This sample did not need to use a transaction since it only manipulates very simple data. It's not doing any work on shared, durable backend data. It's not calling any Resource Managers. But the sample serves as a good starting point.

As a next step you could add code to manipulate a database such as a Microsoft SQL Server database using the Microsoft® ActiveX® Data Objects (ADO) support in WFC. For more information on using ADO from Java see the MTS samples in the Visual J++ 6.0 samples. Microsoft® SQL Server™ provides a Resource Manager and Microsoft Transaction Server takes full advantage of this when the database is accessed from your Java component through ADO, providing a truly robust transaction based server-side Java solution.

Up


Integrating Java and Microsoft Visual Studio

Microsoft® Visual Studio® 6.0 is a complete suite of tools that meets both technical and business requirements for enterprise development. The Visual Studio tools build upon the platform and infrastructure resources of Microsoft® Windows® Distributed interNet Applications Architecture (DNA) to give you what you need to build component-based enterprise systems and provide a robust execution context for deploying and maintaining them.

This section focuses on the Microsoft® Visual J++® component of Visual Studio, its extensibility, the use of Windows Foundation Classes for Java (WFC) with Visual J++, and the use of delegates by WFC components to expose events.

The following Visual Studio documents are provided.

White Papers

Visual Studio 6.0 Extensibility Fundamentals discusses extensibility in Microsoft® Visual Studio® version 6.0 and describes how to create tools, such as add-ins and wizards, to customize your development environment.

Unifying the Programming Model for Windows and the Internet provides and overview of the WFC framework, showing how it unifies the Microsoft® Win32® and Dynamic HTML (DHTML) programming models.

WFC Programming Concepts provides a conceptual framework for the WFC packages and classes and explains some of the fundamental WFC models.

Introduction to WFC Programming provides an overview of WFC and shows how to create a simple WFC application using Visual J++.

Delegates in Visual J++ 6.0 provides an overview of defining and using delegates in Visual J++.

The Truth about Delegates describes the advantages of delegate-based event models and refutes arguments against the use of delegates.

Package Reference

Extensibility in Visual J++ (com.ms.vstudio.dte package) describes additions to the Visual Studio extensibility object model that are specific to Visual J++.

Visual J++ Code Model (com.ms.vstudio.vjext package) describes the COM interfaces that make up the Visual J++ Code Model. These interfaces provide access to information about the code in your current project.


Extensibility in Visual J++

Additions to the Visual Studio extensibility object model specific to Visual J++ are described in this section.

  • VJProject Object, VJProjects Collection

  • VJProjectItem Object, VJProjectItems Collection

  • VJProperty Object, VJProperties Collection

  • Events Object

  • VJProjectEvents Object, VJProjectItemsEvents Object

  • SelectedItem Object, SelectedItems Collection


VJProject Object, VJProjects Collection

The VJProjects collection contains all Visual J++ projects in the solution. You can access just the collection of Visual J++ projects with DTE.VJProjects.

Remarks

The Visual J++ package defines a VJProjects collection that is immediately accessible from the DTE object. This collection contains a project object for each Visual J++ project in the solution.


VJProjectItem Object, VJProjectItems Collection

The VJProjectItem object represents an item contained in a Visual J++ project.

The VJProjectItems collection contains all VJProjectItem objects in the current instance of the development environment.


Code Property

Returns the root object of the model for Java code in a file.

Syntax

Property Get Code() As IJavaCompileUnit


VJProperty Object, VJProperties Collection

The VJProperty object represents a property of another object. It provides a way to associate a property with an object other than accessing it from the object as in object1.property1.

The VJProperties collection contains all currently available VJProperty objects.


Events Object (Visual J++)

Allows access to all events in the extensibility model. Events are also exposed from specific objects within the extensibility model.

Remarks

The extensibility model contains a root Events object from which add-ins can reference objects supporting general, core extensibility events; for example, DTE.Events.SolutionEvents and the AddProject event. From the root Events object add-ins can also reference objects supporting product-specific events, such as DTE.Events.VJProjectItemsEvents and the ItemAdded event. The environment resolves the name VJDesignerEvents in the registry to dynamically find a type library describing the object that supports events.


VJProjectsEvents Property

Returns the VJProjectEvents object.

Syntax

Property Get VJProjectsEvents([filter As Object]) As VJProjectsEvents

Parameters

filter

If not Null, this is a VJProject object indicating that the resulting VJProjectsEvents object will source events only for the specified VJProject object.


VJProjectItemsEvents Property

Returns the VJProjectItemsEvents object.

Syntax

Property Get VJProjectItemEvents([filter As Object]) As VJProjectItemsEvents

Parameters

filter

If not Null, this is a VJProjectItem indicating that the resulting VJProjectItemsEvents object will source events only for the specified VJProject object.


VJProjectsEvents Object

VJProjectsEvents Object, VJProjectItemsEvents Object

These event objects handle the following events:

  • ItemAdded Event

  • ItemRemoved Event

  • ItemRenamed Event


ItemAdded Event (Visual J++)

This event is triggered just after adding a VJProject object.

Syntax

Sub ItemAdded (object As VJProject)

Sub ItemAdded (object As VJProjectItem)

Parameters

object

The item just added.


ItemRemoved Event (Visual J++)

This event is triggered just after removing a VJProject or VJProjectItem.

Syntax

ProjectEvents object:

Sub ItemRemoved (object As VJProject)

ProjectItemsEvents object:

Sub ItemRemoved (object As VJProjectItem)

Parameters

object

The item just removed.


ItemRenamed Event (Visual J++)

This event is triggered just after renaming a VJProject or VJProjectItem.

Syntax

ProjectItemsEvents object:

Event ItemRenamed (object As VJProjectItem, oldname As String)

ProjectEvents object:

Event ItemRenamed (object As VJProject, oldname As String)

Parameters

object

The item just renamed.

oldname

The previous name of the item.


SelectedItem Object, SelectedItems Collection

The SelectedItem object represents a selected project or project item.

The SelectedItems collection returns a collection of all SelectedItem objects.

Remarks

Use the SelectedItem object to get the project with the focus to provide context for an add-in.

A project always has a SelectedItems collection, even when there are no project items selected. For example, if the Project Explorer has focus and a project node is selected, there is one SelectedItem object. That selected item's Project property points to the selected project, and the ProjectItem property is Null.

When a file that is not a project item is selected in the alternate directory view, the SelectedItem.Info field provides the full path to the selected file. The Project and ProjectItem properties are set to Null.

For Visual J++, the following fields are also used:

  • .Name Display name for the item (for example, the name displayed in the Project Explorer)

  • .Info(i) Indexed property of additional information about the selected item. This is used when the alternate directory view is shown in the Project Explorer, and a file is selected that is not in the project. .Info(1) contains the full path to the file.

  • .InfoCount The number of strings in .Info. This is always 1 for Visual J++.


Visual J++ Code Model

The COM interfaces that make up the Visual J++ Code Model provide access to information about the code in your current project. Much of this information is obtained by a background parser that runs under the Visual J++ development environment. This parser continually processes the code, rebuilding parse trees and symbol tables in response to every change. The Code Model interfaces expose the contents of these trees and tables in a format that provides easy programmatic access from any automation-capable language. The interfaces also permit callers to modify the code.

The Visual J++ Code Model is used by various parts of Visual J++, including the source code editor, the class browser, and various wizards and designers. It can also be used by third-party add-ins.

For detailed information, see the following interfaces:

  • IJavaBlockStatement

  • IJavaClass

  • IJavaClasses

  • IJavaCodeSense

  • IJavaCommentTags

  • IjavaCompileUnit

  • IJavaCompileUnitEvents

  • IJavaDocComment

  • IJavaField

  • IJavaImports

  • IJavaInitializer

  • IJavaInitializers

  • IJavaMember

  • IJavaMembers

  • IJavaMethod

  • IJavaPackage

  • IJavaPackages

  • IJavaSourceElement

  • IJavaSourceElements

  • IJavaStatement

See the following for information on:

  • JavaClassModifier, JavaClassModifiers

  • JavaMemberModifier, JavaMemberModifiers

  • JavaNameSearchFlag, JavaNameSearchFlags

  • JavaNamespaceFlag, JavaNamespaceFlags

  • JavaPrototypeFlag, JavaPrototypeFlags

  • JavaReferenceType

  • JavaStatementType


IJavaBlockStatement Interface

The IJavaBlockStatement interface represents a Java block statement (a statement enclosed in braces and containing zero or more statements). It functions as a collection of statements.

IJavaBlockStatement : IJavaStatement

This interface defines the following members.

Member | Description
AddStatementText | Inserts a new statement at the given index.
Count | Returns the number of statements contained in this block.
Item | Returns the IJavaStatement at the given index.
RemoveStatementAt | Removes a statement from the block.


IJavaBlockStatement::AddStatementText

Inserts a new statement at the given index.

Syntax

HRESULT AddStatementText([in] BSTR bstrStatementText, [in] long nIndex);

Sub AddStatementText(ByVal bstrStatementText As String, ByVal nIndex As Long)

This syntax has these parts:

Parameters

bstrStatementText

String. Statement text.

nIndex

The index of the existing member before which the new statement should be inserted. To insert the new statement at the end of the class, pass –1 as the index.


IJavaBlockStatement::Count

This property returns the number of statements contained in this block.

Syntax

[propget] HRESULT Count([out, retval] long* retval);

Property Count As Long


IJavaBlockStatement::Item

This function returns the IJavaStatement at the given index.

Syntax

HRESULT Item([in] VARIANT index, [out, retval] IJavaStatement** ppMember);

Function Item(ByVal index) As IJavaStatement

Parameters

index

Variant reference to IJavaStatement.


IJavaBlockStatement::RemoveStatementAt

Removes a statement from the block as specified by its index.

Syntax

HRESULT RemoveStatementAt([in] long nIndex);

Sub RemoveStatementAt(ByVal nIndex As Long)

Parameters

nIndex

The index of the existing block statement to be removed.


IJavaClass Interface

The IJavaClass interface represents a Java class, interface, or delegate.

IJavaClass : IJavaSourceElement

This interface defines the following members.

Member | Description
AddField | Adds a field class member.
AddMethod | Adds a method class member.
CanOverride | Indicates whether the class can override the given method.
FullName | Represents the fully qualified name of the class.
GetBlockRange | Returns the range of text containing the class.
GetBodyRange | Returns the text range containing the class's body.
GetNameRange | Returns the starting position and length of the range of text containing the class's name.
Initializers | A collection of the initializers declared within the class.
InnerClasses | A collection of the inner classes declared within the class.
Interfaces | A collection of the interfaces that the class implements.
IsClass | Indicates whether the IJavaClass object represents a class.
IsDelegate | Indicates whether the IJavaClass object represents a delegate.
IsInterface | Indicates whether the IJavaClass object represents an interface.
IsSubclassOf | Traverses the superclass chain and compares the superclasses' names with the given string.
Members | A collection of the members of the class, both fields and methods.
Modifiers | Provides access to the class's modifiers.
Name | Represents the simple name of the class.
OverrideMethod | Inserts a method declaration and code stub for the given method.
Package | A reference to the package that contains the class.
RemoveMember | Removes a class member.
Superclass | Provides access to the IJavaClass object representing the class's superclass.
Subclasses | A collection of all classes that inherit from this class.
SuperclassName | Provides access to the name of the class's superclass.


IJavaClass::AddField

Use this method to add a field class member.

Syntax

HRESULT AddField(
[in] JavaMemberModifiers mods,
[in] BSTR pType,
[in] BSTR bstrName,
[in] BSTR bstrInitializer,
[in] long nIndex,
[out, retval] IJavaField** retval
);

Function AddField(ByVal mods As Long, ByVal pType As String, ByVal bstrName As String, ByVal bstrInitializer As String, ByVal nIndex As Long) As IJavaField

Parameters

mods

Modifiers for the new field.

pType

The type signature for the type of the new field.

bstrName

The name of the new field.

bstrInitializer

The initial value expression for the new field. Can be Null.

nIndex

The index of the existing member before which the new field should be inserted. To insert the new field at the end of the class, pass –1 as the index.


IJavaClass::AddMethod

Use this method to add a method class member.

Syntax

HRESULT AddMethod([in] JavaMemberModifiers mods, [in] BSTR bstrSignature,
[in] BSTR bstrName, [in] BSTR bstrFormalNames,
[in] BSTR bstrBodyText, [in] long nIndex,
[out, retval] IJavaMethod** retval);

Function AddMethod(ByVal mods As Long, ByVal bstrSignature As String, ByVal bstrName As String, ByVal bstrFormalNames As String, ByVal bstrBodyText As String, ByVal nIndex As Long) As IJavaMethod

Parameters

mods

Modifiers for the new method.

bstrSignature

The type signature for the new method, including parameter types and the return type.

bstrName

The name of the new method.

bstrFormalNames

A comma-separated list of the formal parameter names for the new method.

bstrBodyText

The text to be inserted as the body of the new method. May be Null.

nIndex

The index of the existing member before which the new method should be inserted. To insert the new method at the end of the class, pass –1 as the index.

Remarks

To add a constructor to a class, call AddMethod with no return type specified in the signature and a name that is identical to the class name.


IJavaClass::CanOverride

Indicates whether the class can override the given method. This is determined by searching the class's methods for a prototype matching the given method. If one is found, or if the given method cannot be overridden for other reasons (for example, it is a final method), VARIANT_FALSE is returned, indicating that a call to OverrideMethod would result in uncompilable code.

Syntax

HRESULT CanOverride(

[in] IJavaMethod* pMethod,
[out, retval] VARIANT_BOOL* retval
);

Property CanOverride(pMethod As IJavaMethod) As Boolean

Parameters

pMethod

Reference to the method to be overridden.


IJavaClass::FullName

The FullName property represents the fully qualified name of the class, including its package name.

Syntax

HRESULT FullName([out, retval] BSTR* retval);

Property Get FullName As String


IJavaClass::GetBlockRange

This method returns the range of text containing the class, including all white space preceding the class declaration. These values are expressed in character units relative to the compile unit's text buffer.

Syntax

HRESULT GetBlockRange(
[out] long* pnStart,
[out] long* pnLength
);

Sub GetBlockRange(pnStart As Long, pnLength As Long)

Parameters

pnStart

Long. Starting position, in character units relative to the compile unit's text buffer.

pnLength

Long. Length, in character units relative to the compile unit's text buffer.


IJavaClass::GetBodyRange

This method returns the text range containing the class's body. The range begins with a left bracket ({) and ends with a right bracket (}). These values are expressed in character units relative to the compile unit's text buffer.

Syntax

HRESULT GetBodyRange(
[out] long* pnStart,
[out] long* pnLength
);

Sub GetBodyRange(pnStart As Long, pnLength As Long)

Parameters

pnStart

Long. Starting position, in character units relative to the compile unit's text buffer.

pnLength

Long. Length, in character units relative to the compile unit's text buffer.


IJavaClass::GetNameRange

This method returns the starting position and length of the range of text containing the class's name. These values are expressed in character units relative to the compile unit's text buffer.

Syntax

HRESULT GetNameRange([out] long* pnStart, [out] long* pnLength);

Sub GetNameRange(pnStart As Long, pnLength As Long)

Parameters

pnStart

Long. Starting position, in character units relative to the compile unit's text buffer.

pnLength

Long. Length, in character units relative to the compile unit's text buffer.


IJavaClass::Initializers

This property is a collection of the initializers declared within the class.

Syntax

HRESULT Initializers([out, retval] IJavaInitializers** retval);

Property Get Initializers As IJavaInitializers


IJavaClass::InnerClasses

This property is a collection of the inner classes declared within the class. This collection includes both nested top-level classes and member classes. It does not include local or anonymous classes.

Syntax

HRESULT InnerClasses([out, retval] IJavaClasses** retval);

Property Get InnerClasses As IJavaClasses


IJavaClass::Interfaces

This property is a collection of the interfaces that the class implements.

Syntax

HRESULT Interfaces([out, retval] IJavaClasses** retval);

Property Get Interfaces As IJavaClasses


IJavaClass::IsClass

This property indicates whether the IJavaClass object represents a class.

Syntax

HRESULT IsClass([out, retval] VARIANT_BOOL* retval);

Property Get IsClass As Boolean


IJavaClass::IsDelegate

This property indicates whether the IJavaClass object represents a delegate.

Syntax

HRESULT IsDelegate([out, retval] VARIANT_BOOL* retval);

Property Get IsDelegate As Boolean


IJavaClass::IsInterface

This property indicates whether the IJavaClass object represents an interface.

Syntax

HRESULT IsInterface([out, retval] VARIANT_BOOL* retval);

Property Get IsInterface As Boolean


IJavaClass::IsSubclassOf

Traverses the superclass chain and compares the superclasses' names with the given string. If a match is found, returns VARIANT_TRUE. The given string can be a fully qualified dotted name or not.

Syntax

[propget] HRESULT IsSubclassOf([in] BSTR bstrName, [out, retval] VARIANT_BOOL* retval);

Property IsSubclassOf(ByVal bstrName As String) As Boolean

Parameters

bstrName

The class name to check.


IJavaClass::Members

This property is a collection of the members of the class, both fields and methods. The collection can be filtered in various ways with the optional parameters passed when getting the property.

Syntax

HRESULT Members(
[in, defaultvalue("")] BSTR bstrName,
[in, defaultvalue(0)] VARIANT_BOOL fIncludeInherited,
[in, defaultvalue(0)] IJavaClass *pWhoIsAsking,
[in, defaultvalue(0)] JavaNamespaceFlags flags,
[out,retval] IJavaMembers **ppMembers
);

Property Members([ByVal bstrName As String], [ByVal fIncludeInherited As Boolean], [pWhoIsAsking As IJavaClass], [ByVal flags As Long]) As IJavaMembers

Parameters

bstrName

Member name to return.

fIncludeInherited

Passing VARIANT_TRUE adds members from all superclasses to the collection.

pWhoIsAsking

Reference to IJavaClass member.

flags

JavaNamespaceFlags flag.

Remarks

The bstrName parameter allows you to retrieve members with a particular name. Passing VARIANT_TRUE for the fIncludeInherited parameter adds members from all superclasses to the collection. Passing an IJavaClass pointer in the pWhoIsAsking parameter excludes members from the collection that are not accessible from the given class. Finally, flags from the JavaNamespaceFlags enumeration can be specified.


IJavaClass::Modifiers

This property provides access to the class's modifiers.

Syntax

[propget] HRESULT Modifiers([out, retval] JavaMemberModifiers* retval);

[propput] HRESULT Modifiers([in] JavaMemberModifiers newval);

Property Modifiers As Long

Parameters

newval

New class modifier value.

Remarks

The modifiers are flag bits drawn from the JavaClassModifier enumeration.


IJavaClass::Name

These properties provide access to the name of the class. The Name property represents the simple name of the class. For example, in the class java.awt.Graphics, the Name property is "Graphics" and the FullName property is "java.awt.Graphics."

Syntax

[propget] HRESULT Name([out, retval] BSTR* retval);

[propput] HRESULT Name([in] BSTR newval);

Property Name As String

Parameters

newval

New class name value.


IJavaClass::OverrideMethod

Inserts a method declaration and code stub for the given method.

Syntax

HRESULT OverrideMethod([in] IJavaMethod* pMethod);

Sub OverrideMethod(pMethod As IJavaMethod)

Parameters

pMethod

The method to be overridden.


IJavaClass::Package

This property is a reference to the package that contains the class.

Syntax

HRESULT Package([out, retval] IJavaPackage** retval);

Property Get Package As IJavaPackage


IJavaClass::RemoveMember

Use this method to remove a class member.

Syntax

HRESULT RemoveMember([in] IJavaMember* pMember);

Sub RemoveMember(pMember As IJavaMember)

Parameters

pMember

The member to remove.


IJavaClass::Subclasses

This property is a collection of all classes that inherit from this class. All classes in installed packages (including those along the CLASSPATH) and in open Visual J++ projects are searched.

Syntax

[propget] HRESULT Subclasses([out, retval] IJavaClasses** retval);

Property Subclasses As IJavaClasses


IJavaClass::Superclass

This property provides access to the IJavaClass object representing the class's superclass.

Syntax

[propget] HRESULT Superclass([out, retval] IJavaClass** retval);

[propput] HRESULT Superclass([in] IJavaClass* newval);

Property Superclass As IJavaClass

Parameters

newval

New superclass name value.


IJavaClass::SuperclassName

This property provides access to the name of the class's superclass.

Syntax

[propget] HRESULT SuperclassName([out, retval] BSTR* retval);

[propput] HRESULT SuperclassName([in] BSTR newval);

Property SuperclassName As String


IJavaClasses Interface

This interface is used to enumerate classes in a compile unit or package, as well as interfaces, inner classes, and subclasses of a class.

IJavaClasses : IDispatch

This interface defines the following members.

Member | Description
Add | Inserts a class or interface into the collection by modifying the code that the collection describes.
Count | The number of classes in the collection.
Item | Returns an IJavaClass item from the collection.
Remove | Removes a class from the collection by modifying the code that the collection describes.


IJavaClasses::Add

Inserts a class or interface into the collection by modifying the code that the collection describes. The method's parameters allow specification of the name of the new class, whether the class or interface keyword should be used, and the position at which to insert the class into the collection.

Syntax

HRESULT Add([in] BSTR bstrName, [in] VARIANT_BOOL bClass, [in] long nIndex, [out, retval] IJavaClass** retval);

Function Add(ByVal bstrName As String, ByVal bClass As Boolean, ByVal nIndex As Long) As IJavaClass

Parameters

bstrName

The name of the new class.

bClass

Indicates whether a new class or interface is to be inserted.

nIndex

The index of the existing class before which the new class is to be inserted. To insert the new class at the end of the collection, pass –1 as the index.


IJavaClasses::Count

Returns the number of classes in the collection.

Syntax

[propget] HRESULT Count([out, retval] long* pCount);

Property get Count As Long


IJavaClasses::Item

Returns an IJavaClass item from the collection. The VARIANT index provided specifies either the name of a class to search for or an integer index.

Syntax

HRESULT Item(
[in] VARIANT index,
[out, retval] IJavaClass** ppClass
);

Function Item(ByVal index) As IJavaClass

Parameters

index

Variant. Class item index or name of a class.


IJavaClasses::Remove

Removes a class from the collection by modifying the code that the collection describes.

Syntax

HRESULT Remove(
[in] IJavaClass* pClass);

Sub Remove(pClass As IJavaClass)

Parameters

pClass

Reference to the class in the collection that is to be removed.


IJavaCodeSense Interface

The IJavaCodeSense interface is the primary interface for the Visual J++ Code Model service, providing global Code Model operations. It is accessed by calling IServiceProvider::QueryService, with SID_SJavaCodeSense as the service ID:

hr = QueryService (SID_SJavaCodeSense, IID_IJavaCodeSense, &pICS)

This interface defines the following members.

Member | Description
Class | Returns a class object for the named class, searching relative to the given base directory.
CompileUnit | Provides access to a compile unit.
Package | Returns a package object for the named package, searching relative to the given base directory.
SourceElements | Provides access to a collection of one or more named Java source code elements.


IJavaCodeSense::Class

Returns a class object for the named class, searching relative to the given base directory.

Syntax

HRESULT Class(
IJavaClass* BSTR bstrName,
[in] BSTR bstrCurrentDirectory,
[in] DWORD dwFlags
);

Property Class(ByVal bstrClassName As String, ByVal bstrCurrentDirectory As String, ByVal dwFlags As Unsigned Long) As IJavaClass

Parameters

bstrClassName

Required string. Class name specification.

bstrCurrentDirectory

String. Current directory file specification.

dwFlags

JavaNamespaceFlags flag.

Remarks

Classes are identified by their names within the Java namespace (such as java.lang.String). An optional directory name parameter specifies a starting point for locating the requested class. This directory is substituted for the period wherever it appears in the CLASSPATH.


IJavaCodeSense::CompileUnit

This property provides access to a compile unit (a file containing one or more classes).

Syntax

HRESULT CompileUnit(
[in]BSTR bstrFileName,
IJavaCompileUnit* retval
);

Property Get CompileUnit(FileName As String) As IJavaCompileUnit

Parameters

FileName

Required string. A full path that identifies the file containing one or more classes.

Remarks

Compile units are identified by fully qualified file names (for example, C:\MyClasses\Foo.java). The file name extension is optional. If a file with the .java extension does not exist, but a corresponding .class file does exist, the Visual J++ Code Model uses the .class file to obtain as much information as possible about the class.

Return Value

The following value is obtained from the returned HRESULT.

Return value | Meaning
S_OK | Success


IJavaCodeSense::Package

Returns a package object for the named package, searching relative to the given base directory.

Syntax

HRESULT Package(
[in] BSTR bstrName
[in] VARIANT_BOOL fPublic
[in] BSTR bstrDir
[out, retval]IJavaPackage** retval
);

Property Get CompileUnit(FileName As String) As IJavaCompileUnit

Parameters

FileName

Required string. A full path that identifies the file containing one or more classes.

fPublic

Boolean. Determines if this property is public.

bstrDir

String. Base directory.

Remarks

Packages are identified by their names within the Java namespace (such as java.lang). An optional directory name parameter specifies a starting point for locating the requested package. This directory is substituted for a period wherever it appears in the CLASSPATH.


IJavaCodeSense::SourceElements

This property provides access to a collection of one or more named Java source code elements (classes, methods, or fields).

Syntax

HRESULT SourceElements(
[in] BSTR bstrName
[in] JavaNameSearchFlags flags
[out, retval]IJavaSourceElements **retval
);

Property SourceElements(ByVal bstrName As String, ByVal flags As Long) As IjavaSourceElements

Parameters

bstrName

Required string. Class, method, or field.

flags

Long. Reserved for internal use only.

Remarks

If more than one element matches the specified name (for example, overloaded methods and classes in different packages), all such source elements are included in the returned collection.


IJavaCommentTags Interface

This is a generic interface to the four types of tag collections in a documentation comment (Links, Authors, Params, and Exceptions). It provides standard enumeration methods as well as Add/Remove methods for manipulating the content.

IJavaCommentTags : IDispatch

This interface defines the following members.

Member | Description
Add | Adds a new tag to the collection.
Count | Provides the number of tags in the collection.
Item | Looks up a tag by name or index.
Remove | Removes a tag from the collection.


IJavaCommentTags::Add

Adds a new tag to the collection. The tag name is the same as the others in the collection.

Syntax

HRESULT Add(
[in] BSTR bstrText,
[in] long nIndex
);

Sub Add(ByVal bstrText As String, ByVal nIndex As Long)

Parameters

bstrText

String. Tag text.

nIndex

The index of the existing member before which the new comment tag should be inserted. To insert the new comment tag at the end of the class, pass –1 as the index.


IJavaCommentTags::Count

This property provides the number of tags in the collection.

Syntax

[propget] HRESULT Count([out, retval] long* retval);

Property Count as Long


IJavaCommentTags::Item

The VARIANT index provided specifies either an integer index in the collection or a name which searches the first word following the tag.

Syntax

HRESULT Item(
[in] VARIANT varIndex,
[out, retval] BSTR* retval

);

Function Item(ByVal varIndex) As String

Parameters

varIndex

Specifies either an integer index in the collection or a name.

Remarks

The named lookup is useful in the Params and Exceptions collections because the first word contains the name of the parameter or type of exception, which is subsequently documented.


IJavaCommentTags::Remove

Removes a tag from the collection. Either a string or an integer can be passed in the VARIANT parameter.

Syntax

HRESULT Remove([in] VARIANT varIndex);

Sub Remove(ByVal varIndex)

Parameters

varIndex

String or integer identifying the tag in the collection to be removed.


IJavaCompileUnit Interface

This interface represents a Java compile unit (source file).

public interface IJavaCompileUnit extends Iunknown

The interface contains the following properties and methods.

Method | Description
Classes | Collection of the classes in the compile unit.
ClassFromType | Returns a class for a given type signature.
FindPackage | Returns the package with a specified name.
Imports | Collection of the compile unit's imported package and class names.
Package | Provides access to the package containing this compile unit.
PackageName | Name of the package containing this compile unit.
PrimaryClassName | The name of the primary (public) class in this compile unit.
ProjectItem | The VJProjectItem that corresponds to the compile unit.
ReadOnly | Indicates whether the compile unit can be modified.
SaveChanges | Commits changes that have been made in the compile unit, unless the user has the file open for editing.
TextFromType | Converts a type signature to a type name in text format.
TypeFromText | Converts a type name in text format to a type signature.


IJavaCompileUnit::Classes

Provides access to the classes that are contained in the compile units of the package.

Syntax

HRESULT Classes(
[out, retval] IJavaClasses** retval
);

Property Classes As IJavaClasses


IJavaCompileUnit::ClassFromType

This property returns the class corresponding to the given type signature.

Syntax

HRESULT ClassFromType([in] BSTR bstrType, [out, retval] IJavaClass** retval);

Function ClassFromType(ByVal bstrType As String) As IJavaClass

Parameters

bstrType

The type signature.

Remarks

If the type signature represents a primitive type or an array type, the property access fails.


IJavaCompileUnit::FindPackage

This method returns the package that contains a class with the given name. The name resolution is performed relative to the namespace of the compile unit.

Syntax

HRESULT FindPackage([in] BSTR bstrName,
[out, retval] IJavaPackage** retval);

Function FindPackage(ByVal bstrName As String) As IJavaPackage

Parameters

bstrName

The name of the requested package.


IJavaCompileUnit::Imports

This property is a collection of the names of packages and classes that are imported by the compile unit. For a compiled .class file, this collection is always empty.

Syntax

HRESULT Imports(
[out, retval] IJavaImports** retval
);

Property Imports As IJavaImports


IJavaCompileUnit::Package

Provides access to the name of the IJavaPackage object representing the package for the compile unit.

Syntax

HRESULT Package([out, retval] IJavaPackage** retval);

Property Get Package As IJavaPackage


IJavaCompileUnit::PackageName

Provides access to the name of the compile unit's package.

Syntax

HRESULT PackageName([out, retval] BSTR* retval);

Property Get PackageName As String


IJavaCompileUnit::PrimaryClassName

This property is the name of the primary class in the compile unit.

Syntax

HRESULT PrimaryClassName([out, retval] BSTR* retval);

Property Get PrimaryClassName As String

Remarks

This is the name of the class that matches the compile unit's file name (typically, this is the public class in the compile unit).


IJavaCompileUnit::ProjectItem

This read-only property returns the VJProjectItem that corresponds to the IJavaCompileUnit, if there is one. This property returns Null if the IJavaCompileUnit does not represent a file of Java code that is part of the current solution.

Property ProjectItem As VJExt._VJProjectItem


IJavaCompileUnit::ReadOnly

This property indicates whether the compile unit can be modified.

Syntax

HRESULT ReadOnly(
VARIANT_BOOL* retval
);

Property ReadOnly As Boolean

Remarks

A compile unit can be read-only if source code is not available.


IJavaCompileUnit::SaveChanges

This method causes any changes to the compile unit's text buffer to be saved to disk.

Syntax

HRESULT SaveChanges();

Sub SaveChanges

Remarks

If the buffer is successfully saved, the method returns S_OK. If the buffer is currently locked for editing (for example, open in an editor window), the buffer is not saved, and the method returns S_FALSE. If the buffer cannot be saved for any other reason, the method fails.


IJavaCompileUnit::TextFromType

This property returns the textual (Java source code) representation of a type that corresponds to a type signature. The given type signature must conform to the Java signature format. A parameter specifies whether a class name appearing in the returned name should be expressed as a simple name or as a fully qualified name.

Syntax

HRESULT TextFromType(
[in] JavaReferenceType jrt,
[in] IJavaCompileUnit* pUnitDest,
[in] BSTR bstrType,
[out, retval] BSTR* retval
);

Function TextFromType(ByVal jrt As JavaReferenceType, pUnitDest As IJavaCompileUnit, bstrType As String) As String

Parameters

jrt

Flag from the JavaReferenceType enumeration specifying the type of reference name requested.

pUnitDest

The compile unit in which the type name is used. Can be Null.

bstrType

The type signature to be converted.


IJavaCompileUnit::TypeFromText

This property returns the type signature that corresponds to the textual (Java source code) representation of a type.

Syntax

HRESULT TypeFromText(
[in] BSTR bstrText,
[out, retval] BSTR* retval
);

Function TypeFromText(ByVal bstrText As String) As String

Parameters

bstrText

The type name to be converted.

Remarks

The given textual type can represent a primitive type (such as char, short), a class type (for example, String), or an array type (such as int[]). The given textual type is interpreted relative to the namespace of the compile unit, which includes all imported classes and all other classes in the compile unit's package. The returned type signature conforms to the Java signature format, as defined in Section 2.2, Lexical Grammar, in the Java Virtual Machine Specification.


IJavaCompileUnitEvents Interface

This is an outgoing (event) interface provided by the compile unit through the standard connection point mechanism. If you implement this interface, you can query the compile unit object for IID_IConnectionPointContainer and then call its FindConnectionPoint method with IID_IJavaCompileUnitEvents as the parameter. On the connection point interface returned, you can pass your event handler to the Advise method.

Note You can also register to receive this event from the CodeSense object. In this case, your handler will receive events from all compile units.

This interface defines the following members.

Member | Description
OnCompileUnitChanged | Signals that the objects created by the parser may have changed.


IJavaCompileUnitEvents::OnCompileUnitChanged

This event is fired immediately after the CodeSense parser runs. It signals that the objects created by the parser may have changed.

Syntax

HRESULT OnCompileUnitChanged(
[in]IJavaCompileUnit* pUnit);
};

Sub OnCompileUnitChanged(pUnit As IJavaCompileUnit)

Parameters

pUnit

The compile unit that has changed.

Remarks

The parser runs only when information is requested from a CodeSense object, and the buffer has been changed since the last parse.


IJavaDocComment Interface

The IJavaDocComment interface represents the documentation comment for a class, field, or method.

IJavaDocComment : Idispatch

A documentation comment is delimited by /** and */ and immediately precedes the source element that it describes. For a description of the contents of documentation comments, see Chapter 18 of The Java Language Specification.

This interface defines the following members.

Member | Description
Authors | Provides access to the first of @author tags in the documentation comment.
Description | Provides access to all text preceding the first tag in the comment.
Exception | Provides access to the list of @exception tags in the documentation comment.
Links | Provides access to the list of @see tags in the documentation comment.
Params | Provides access to the list of @param tags in the documentation comment.
RemoveTag | Deletes a tag with the given name.
Return | Provides access to the @return tag in the documentation comment.
Summary | Provides access to the summary sentence of the documentation comment.
TagValue | Allows read/write access to the tag with the given name.
Text | Provides access to the complete text of the documentation comment.
Version | Provides access to the @version tag in the documentation comment.


IJavaDocComment::Authors

This property provides access to the list of @author tags in the documentation comment.

Syntax

[propget] HRESULT Authors([out, retval] IJavaCommentTags** retval);

Property Authors As IJavaCommentTags


IJavaDocComment::Description

This property provides access to all text preceding the first tag in the comment, including the summary sentence.

Syntax

[propget] HRESULT Description([out, retval] BSTR* retval);

[propput] HRESULT Description([in] BSTR newval);

Property Description As String


IJavaDocComment::Exceptions

This property provides access to the list of @exception tags in the documentation comment.

Syntax

[propget] HRESULT Exceptions([out, retval] IJavaCommentTags** retval);

Property Exceptions As IJavaCommentTags


IJavaDocComment::Links

This property provides access to the list of @see tags in the documentation comment.

Syntax

[propget] HRESULT Links([out, retval] IJavaCommentTags** retval);

Property Links as IJavaCommentTags


IJavaDocComment::Params

This property provides access to the list of @param tags in the documentation comment.

Syntax

[propget] HRESULT Params([out, retval] IJavaCommentTags** retval);


IJavaDocComment::RemoveTag

This method deletes a tag with the given name.

Syntax

HRESULT RemoveTag([in] BSTR bstrTagName);

Sub RemoveTag(ByVal bstrTagName As String)

Parameters

bstrTagName

The name of the tag to be removed.


IJavaDocComment::Return

This property provides access to the @return tag in the documentation comment.

Syntax

[propget] HRESULT Return([out, retval] BSTR* retval);

[propput] HRESULT Return([in] BSTR newval);

Property Return As String

Parameters

newVal

The new return tag string value.


IJavaDocComment::Summary

This property provides access to the summary sentence of the documentation comment.

Syntax

[propget] HRESULT Summary([out, retval] BSTR* retval);

Property Get Summary As String


IJavaDocComment::TagValue

This property allows read/write access to the tag with the given name.

Syntax

[propget] HRESULT TagValue([in] BSTR bstrTagName, [out, retval] BSTR* retval);

[propput] HRESULT TagValue([in] BSTR bstrTagName, [in] BSTR newval);

Property TagValue(ByVal As String) As String

Parameters

bstrTagName

The return tag string value.

newVal

The new tag string value.


IJavaDocComment::Text

This property provides access to the complete text of the documentation comment. Leading spaces and asterisks are stripped from the beginning of each line.

Syntax

[propget] HRESULT Text([out, retval] BSTR* retval);

[propput] HRESULT Text([in] BSTR newval);

Property Text As String

Parameters

newVal

The new text string value.


IJavaDocComment::Version

This property provides access to the @version tag in the documentation comment.

Syntax

[propget] HRESULT Version([out, retval] BSTR* retval);

[propput] HRESULT Version([in] BSTR newval);

Property Version As String

Parameters

newVal

The new version string value.


IJavaField Interface

The IJavaField interface represents a field of a class. IJavaField is derived from IJavaMember.

interface IJavaField : IJavaMember

This interface defines the following members.

Member | Description
InitialValue | Provides access to the field's initial value expression.


IJavaField::InitialValue

This property provides access to the field's initial value expression, if any. If the field has no initial value, the InitialValue property is Null.

Syntax

[propget] HRESULT InitialValue([out, retval] BSTR* retval);

[propput] HRESULT InitialValue([in] BSTR newval);

Property InitialValue As String


IJavaImports Interface

The IJavaImports interface allows enumeration of the import statements in a compile unit.

IJavaImports : IDispatch

This interface defines the following members.

Member | Description
Add | Adds an import statement for the given package to the compile unit.
Count | Returns the number of import statements in the compile unit.
Item | Returns the name of the imported package or class at the given index.
Remove | Removes the given import statement from the compile unit.


IJavaImports::Add

This function adds an import statement for the given package to the compile unit.

Syntax

HRESULT Add([in] BSTR bstrName);

Sub Add(ByVal bstrName As String)

Parameters

bstrName

The import statement string value to be added.


IJavaImports::Count

This property returns the number of import statements in the compile unit.

Syntax

[propget] HRESULT Count([out, retval] long* pCount);

Property Count As Long


IJavaImports::Item

This function returns the name of the imported package or class at the given index.

Syntax

HRESULT Item([in] VARIANT index, [out, retval] BSTR* retval);

Function Item(ByVal index) As String

Parameters

index

The reference within the package or class.


IJavaImports::Remove

Removes the given import statement from the compile unit.

Syntax

HRESULT Remove([in] BSTR bstrName);

Sub Remove(ByVal bstrName As String)


IJavaInitializer Interface

The IJavaInitializer interface represents a static or instance initializer.

IJavaInitializer : IJavaSourceElement

This interface defines the following members.

Member | Description
Body | Allows enumeration of the statements that make up the initializer.
Modifiers | Provides access to the initializer's modifiers.


IJavaInitializer::Body

This property allows enumeration of the statements that make up the initializer.

Syntax

[propget] HRESULT Body([out, retval] IJavaBlockStatement** retval);

Property Body As IJavaBlockstatement


IJavaInitializer::Modifiers

This property provides access to the initializer's modifiers. The only relevant bit flag for an initializer is JMM_STATIC.

Syntax

[propget] HRESULT Modifiers([out, retval] JavaMemberModifiers* retval);

Property Modifiers As Long


IJavaInitializers Interface

The IJavaInitializers interface allows enumeration of the class initializers.

IJavaInitializers : IDispatch

This interface defines the following members.

Member | Description
Count | Returns the number of initializers in the class.
Item | Returns the IJavaInitializer at the given index.


IJavaInitializers::Count

This property returns the number of initializers in the class.

Syntax

[propget] HRESULT Count([out, retval] long* pCount);

Property Count As Long


IJavaInitializers::Item

This function returns the IJavaInitializer at the given index.

Syntax

HRESULT Item([in] VARIANT index, [out, retval] IJavaInitializer** retval);

Function Item(ByVal index) As IJavaInitializer


IJavaMember Interface

The IJavaMember interface represents a member (field or method) of a class.

IJavaMember : IJavaSourceElement

This interface defines the following members.

Member | Description
Class | Provides access to the class containing the member.
GetBlockRange | Returns the range of text containing the member.
GetNameRange | Returns the starting position and length of the range of text containing the member's name.
IsField | Indicates that the member is a field.
IsMethod | Indicates that the member is a method.
Modifiers | Provides access to the member's modifiers.
Name | Provides access to the member's name.
Type | Provides access to the member's type.


IJavaMember::Class

This property provides access to the class containing the member.

Syntax

[propget] HRESULT Class([out, retval] IJavaClass** retval);

Property Class As IJavaClass


IJavaMember::GetBlockRange

This method returns the range of text containing the member, including all white space preceding the member declaration. These values are expressed in character units relative to the compile unit's text buffer.

Syntax

HRESULT GetBlockRange([out] long* pnStart, [out] long* pnLength);

Sub GetBlockRange(pnStart As Long, pnLength As Long)

Parameters

pnStart

Long. Starting position, in character units relative to the compile unit's text buffer.

pnLength

Long. Length, in character units relative to the compile unit's text buffer.


IJavaMember::GetNameRange

This method returns the starting position and length of the range of text containing the member's name. These values are expressed in character units relative to the compile unit's text buffer.

Syntax

HRESULT GetNameRange([out] long* pnStart, [out] long* pnLength);

Sub GetNameRange(pnStart As Long, pnLength As Long)

Parameters

pnStart

Long. Starting position, in character units relative to the compile unit's text buffer.

pnLength

Long. Length, in character units relative to the compile unit's text buffer.


IJavaMember::IsField

This property indicates that the member is a field.

Syntax

[propget] HRESULT IsField([out, retval] VARIANT_BOOL* retval);

Property IsField As Boolean


IJavaMember::IsMethod

This property indicates that the member is a method.

Syntax

[propget] HRESULT IsMethod([out, retval] VARIANT_BOOL* retval);

Property IsMethod As Boolean


IJavaMember::Modifiers

This property provides access to the member's modifiers.

Syntax

[propget] HRESULT Modifiers([out, retval] JavaMemberModifiers* retval);

[propput] HRESULT Modifiers([in] JavaMemberModifiers newval);

Property Modifiers As Long

The modifiers flags are specified in the JavaMemberModifier enumeration.


IJavaMember::Name

This property provides access to the member's name.

Syntax

[propget] HRESULT Name([out, retval] BSTR* retval);

[propput] HRESULT Name([in] BSTR newval);

Property Name As String


IJavaMember::Type

This property provides access to the member's type. For a method, the Type property indicates the method's return type. The type is returned as a type signature string. If the member is a constructor method, the type signature is an empty string.

Syntax

[propget] HRESULT Type([out, retval] BSTR* retval);

[propput] HRESULT Type([in] BSTR newval);

Property Type As String


IJavaMembers Interface

This interface is used to enumerate class members.

IJavaMembers : IDispatch

This interface defines the following members.

Member | Description
Count | The number of members in the collection.
Item | The VARIANT index provided specifies an integer index in the collection.
UniqueField | Returns the field of the specified name.


IJavaMembers::Count

The number of members in the collection.

Syntax

[propget] HRESULT Count([out, retval] long* pCount);

Property Count As Long


IJavaMembers::Item

The VARIANT index provided specifies an integer index in the collection.

Syntax

HRESULT Item([in] VARIANT index, [out, retval] IJavaMember** ppMember)

Function Item(ByVal index) As IJavaMember

Parameters

index

Variant. Integer index of the item in the collection.


IJavaMembers::UniqueField

If the collection represents all overloaded class members with the same name (differing only by their parameters), it contains at most one field element because there can only be one field with any given name. This method returns that field, if any.

Syntax

[propget] HRESULT UniqueField ([out,retval] IJavaField **ppField);

Property UniqueField As IJavaField


IJavaMethod Interface

The IJavaMethod interface represents a method of a class.

interface IJavaMethod : IJavaMember

This interface defines the following members.

Member | Description
AddFormal | Adds a new formal parameter to the method.
Body | Provides access to the block statement that makes up the body of the method.
Exceptions | A collection of the exceptions this method can throw.
FormalCount | Provides access to the count of the method's formal parameters.
FormalNameAt | Provides access to the name of one of the method's formal parameters.
FormalNames | Provides access to the method's list of formal parameter names.
FormalTypeAt | Provides access to the type of one of the method's formal parameters.
FormalTypes | Provides access to the method's list of formal parameter types.
GetBodyRange | Returns the range of text containing the method's block statement.
IsConstructor | Indicates whether the method is a constructor.
RemoveFormal | Removes a formal parameter from the method.
RemoveFormalAt | Removes a formal parameter from the method using the index.


IJavaMethod::AddFormal

Adds a new formal parameter to the method.

Syntax

HRESULT AddFormal([in] BSTR pType, [in] BSTR bstrName, [in] long nIndex);

Sub AddFormal(ByVal pType As String, ByVal bstrName As String, ByVal nIndex As Long)

Parameters

pType

The type signature for the new formal parameter.

bstrName

The name of the new formal parameter.

nIndex

The index of the existing formal parameter after which to insert the new one. To insert a new parameter at the end of the list, pass –1 as the index.


IJavaMethod::Body

This property provides access to the block statement that makes up the body of the method.

Syntax

[propget] HRESULT Body([out, retval] IJavaBlockStatement** retval);

Property Body As IJavaBlockStatement


IJavaMethod::Exceptions

This property is a collection of the exceptions this method can throw.

Syntax

[propget] HRESULT Exceptions([out, retval] IJavaClasses** retval);

Property Exceptions As IJavaClasses


IJavaMethod::FormalCount

Provides access to the count of the method's formal parameters.

Syntax

HRESULT FormalCount([out, retval] long* retval);

Property Get FormalCount As Long


IJavaMethod::FormalNameAt

Provides access to the name of one of the method's formal parameters.

Syntax

[propget] HRESULT FormalNameAt([in] long nIndex, [out, retval] BSTR* retval);

[propput] HRESULT FormalNameAt([in] long nIndex, [in] BSTR newval);

Property FormalNameAt(ByVal nIndex As Long) As String

Parameters

nIndex

Long. Reference to the location of the formal parameter.


IJavaMethod::FormalNames

Provides access to the method's list of formal parameter names. The formal names are returned as a comma-separated list.

Syntax

HRESULT FormalNames([out, retval] BSTR* retval);

Property Get FormalNames As String


IJavaMethod::FormalTypeAt

Provides access to the type of one of the method's formal parameters. The formal type is returned in type signature format.

Syntax

[propget] HRESULT FormalTypeAt([in] long nIndex, [out, retval] BSTR* retval);

[propput] HRESULT FormalTypeAt([in] long nIndex, [in] BSTR newval);

Property FormalTypeAt(ByVal nIndex As Long) As String

Parameters

nIndex

Long. Reference to the location of the formal parameter.


IJavaMethod::FormalTypes

Provides access to the method's list of formal parameter types. The formal types are returned in type signature format.

Syntax

HRESULT FormalTypes([out, retval] BSTR* retval);

Property Get FormalTypes As String


IJavaMethod::GetBodyRange

This method returns the range of text containing the method's block statement, beginning with a left bracket ({) and ending with a right bracket (}). These values are expressed in character units relative to the compile unit's text buffer.

Syntax

HRESULT GetBodyRange([out] long* pnStart, [out] long* pnLength);

Sub GetBodyRange(pnStart As Long, pnLength As Long)

Parameters

pnStart

Long. Position in the text buffer to begin.

pnLength

Long. Block statement length in characters.


IJavaMethod::IsConstructor

This property indicates whether the method is a constructor.

Syntax

Property get IsConstructor As Boolean.


IJavaMethod::RemoveFormal

Removes a formal parameter from the method.

Syntax

HRESULT RemoveFormal([in] BSTR bstrName);

Sub RemoveFormal(ByVal bstrName As String)

Parameters

bstrName

The name of the formal parameter to remove.


IJavaMethod::RemoveFormalAt

Removes a formal parameter from the method.

Syntax

HRESULT RemoveFormalAt([in] long nIndex);

Sub RemoveFormalAt(ByVal nIndex As Long)

Parameters

nIndex

The index of the formal parameter to remove.


IJavaPackage Interface

The IJavaPackage interface represents a Java package. A package is a directory in the file system or an installed archive, such as a .cab or .zip file. A package is a collection of compile units and a collection of subpackages. A package also has a name and can have a parent package.

This interface defines the following members.

Member | Description
Classes | A collection of the classes in the package.
Name | Provides access to the name of the package in the Java namespace.
ParentPackage | Provides access to the parent package of the package.
SubPackages | A collection of the subpackages in the package.


IJavaPackage::Classes

This property is a collection of the classes in the package.

HRESULT Classes(
[out, retval] IJavaClasses** retval
);

Property Classes As IJavaClasses


IJavaPackage::Name

This property provides access to the name of the package in the Java namespace, for example, java.lang.

Syntax

HRESULT Name(
[out, retval] BSTR* retval);

Property Name As String


IJavaPackage::ParentPackage

This property provides access to the parent package of the package.

Syntax

HRESULT ParentPackage(
[out, retval] IJavaPackage** retval
);

Property ParentPackage As IJavaPackage

The parent package is contained in the parent directory of the package directory. The parent package is Null if the current package is a top-level package.


IJavaPackage::SubPackages

This property is a collection of the subpackages in the package.

Syntax

SubPackages(
[out, retval] IJavaPackages** retval
);

Property SubPackages As IJavaPackages


IJavaPackages Interface

This interface represents a collection of package collections.

This interface defines the following members.

Member | Description
Count | Returns the number of packages in the collection.
Item | Returns one package in the collection of packages.
PackageNamed | Returns the named package in the collection of packages.


IJavaPackages::Count

This property returns the number of packages in the collection.

Syntax

HRESULT Count([out, retval] long* retval);

Property Get Count As Long


IJavaPackages::Item

This method returns one package in the collection of packages. The VARIANT index provides lookup by name or by integer index.

Syntax

HRESULT Item(
[in] VARIANT index,
[out, retval] IJavaPackage** retval
);

Function Item(ByVal index) As IJavaPackage

Parameters

index

Variant. Reference to the package by name or by integer index.


IJavaPackages::PackageNamed

This property returns the named package in the collection of packages.

Syntax

HRESULT PackageNamed(
[in] BSTR bstrName,
[out, retval] IJavaPackage **retval
);

Property get PackageNamed(ByVal bstrName As String) As IJavaPackage

Parameters

bstrName

String. Package name.


IJavaSourceElement Interface

The IJavaSourceElement interface is a common interface shared by the interfaces that represent various elements appearing within a compile unit. These interfaces include IJavaClass, IJavaMember, and IJavaStatement.

IJavaSourceElement : Idispatch

This interface defines the following members.

Member | Description
CompileUnit | Refers to the compile unit that contains the source element.
DocComment | Provides access to the member's documentation comment.
GetRange | Returns the starting position and length of the range of text containing the source element.
IsValid | Indicates whether the source element object is valid.
Prototype | Returns a prototype string for the source element.


IJavaSourceElement::CompileUnit

This property refers to the compile unit that contains the source element.

Syntax

[propget] HRESULT CompileUnit([out, retval] IJavaCompileUnit** retval);

Property CompileUnit As IJavaCompileUnit


IJavaSourceElement::DocComment

This property provides access to the member's documentation comment, if any.

Syntax

HRESULT DocComment([out, retval] IJavaDocComment** retval);

Property Get DocComment As IJavaDocComment


IJavaSourceElement::GetRange

This method returns the starting position and length of the range of text containing the source element. These values are expressed in character units relative to the compile unit's text buffer.

Syntax

HRESULT GetRange(
[out] long* pnStart,
[out] long* pnLength
);

Sub GetRange(pnStart As Long, pnLength As Long)

Parameters

pnStart

Long. Starting position, in character units relative to the compile unit's text buffer.

pnLength

Long. Length, in character units relative to the compile unit's text buffer.


IJavaSourceElement::IsValid

This property indicates whether the source element object is valid.

Syntax

[propget] HRESULT IsValid([out, retval] VARIANT_BOOL* retval);

Property Get IsValid As Boolean

Remarks

A source element object may be invalid if the compile unit has been changed since the source element was first accessed.


IJavaSourceElement::Prototype

This property returns a prototype string for the source element.

Syntax

HRESULT Prototype([in] JavaPrototypeFlags jpf, [out, retval] BSTR* retval);

Property Get Prototype(ByVal jpf As Long) As String

Parameters

jpf

Formatting flags in JavaPrototypeFlag for the prototype string

Remarks

The property takes a JavaPrototypeFlags parameter that describes which components of the prototype to include.


IJavaSourceElements Interface

The IJavaSourceElements interface allows enumeration of source elements. This collection is returned by the SourceElements property of the IJavaCodeSense interface.

interface IJavaSourceElements : Idispatch

This interface defines the following members.

Member | Description
Count | Returns the number of source elements in the collection.
Item | Returns the source element at the given index.


IJavaSourceElements::Count

This property returns the number of source elements in the collection.

Syntax

[propget] HRESULT Count([out, retval] long* pCount);

Property Count As Long


IJavaSourceElements::Item

This function returns the source element at the given index.

Syntax

HRESULT Item([in] VARIANT index, [out, retval] IJavaSourceElement** retval);

Function Item(ByVal index) As IJavaSourceElement

Parameters

index

Variant. Reference into the SourceElements collection for the specified source element.


IJavaStatement Interface

The IJavaStatement interface represents a statement within a Java method or initializer.

IJavaStatement : IJavaSourceElement

This interface defines the following members.

Member | Description
StatementType | Indicates the type of statement.
Text | Provides access to the text of the statement.


IJavaStatement::StatementType

This property indicates the type of statement.

Syntax

[propget] HRESULT StatementType([out, retval] JavaStatementType* retval);

Property StatementType As JavaStatementType

The statement type is a value from the JavaStatementType set:


IJavaStatement::Text

This property provides access to the text of the statement.

Syntax

HRESULT Text([out, retval] BSTR* retval);

Property Text As String


JavaClassModifier, JavaClassModifiers

The following flags are associated with this enumeration.

JCM_PUBLIC

JCM_PRIVATE

JCM_PROTECTED

JCM_FINAL

JCM_ABSTRACT


JavaMemberModifier, JavaMemberModifiers

The following flags are associated with this enumeration.

Flag | Equals
JMM_PUBLIC | 0x0001
JMM_PRIVATE | 0x0002
JMM_PROTECTED | 0x0004
JMM_STATIC | 0x0008
JMM_FINAL | 0x0010 (valid only for methods)
JMM_SYNCHRONIZED | 0x0020
JMM_VOLATILE | 0x0040 (valid only for fields)
JMM_TRANSIENT | 0x0080 (valid only for fields)
JMM_NATIVE | 0x0100 (valid only for methods)
JMM_ABSTRACT | 0x0400 (valid only for methods)


JavaNamespaceFlag, JavaNamespaceFlags

The following flags are associated with this enumeration:

JNF_TYPESONLY | Internal use only
JNF_STATICONLY | Only static class members
JNF_INVOLVED | Internal use only
JNF_IMPORT | Names that can appear in import statements
JNF_PACKAGE | Internal use only
JNF_INCLUDEPROTECTED | Internal use only
JNF_CONSTRUCTORSONLY | Constructors only


JavaNameSearchFlag, JavaNameSearchFlags

The following flags are associated with this enumeration:

JNSF_NOOPENPROJECTS | No open projects
JNSF_NOINSTALLEDPACKAGES | No installed packages
JNSF_NOCLASSES | No classes
JNSF_NOMEMBERS | No members


JavaPrototypeFlag, JavaPrototypeFlags

The following flags are associated with this enumeration:

JPF_NONAME | Do not include the element's name in the prototype. It is mutually exclusive with JPF_CLASSNAME and JPF_FULLNAME
JPF_CLASSNAME | If the element is a member of a class, prepend the name of the class and a period to the name.
JPF_FULLNAME | Prepend the package name to the class name. Includes JPF_CLASSNAME.
JPF_TYPE | For class members, include the type of the field or return type of the method before its name.
JPF_PARAMTYPES | For methods, include a list of parameter types in parentheses after the name.
JPF_PARAMNAMES | Include the parameter names.
JPF_FULLTYPENAMES | Specifies fully qualified names for reference types included in the prototype.


JavaReferenceType

The following flags are associated with this enumeration:

Flag | Equals
JRT_NOCHANGE | Leave the class name exactly the way it appears in the source code.
JRT_SHORT | Return the simple class name with no package name.
JRT_FULL | Return the full class name, including package name, if any.
JRT_MINIMAL | Return the shortest possible class name; if the class is imported by the compile unit, or is part of the same package as the compile unit, return the simple class name; otherwise, return the full class name.


JavaStatementType Enumeration

JavaStatementType

The following flags are associated with this enumeration:

Flag | Equals
JST_EMPTY | 0,
JST_BLOCK | 1,
JST_IF | 2,
JST_WHILE | 3,
JST_DO | 4,
JST_FOR | 5,
JST_SWITCH | 6,
JST_CASE | 7,
JST_TRY | 8,
JST_BREAK | 9,
JST_CONTINUE | 10,
JST_LABEL | 11,
JST_RETURN | 12,
JST_THROW | 13,
JST_SYNC | 14,
JST_EXPRESSION | 15,
JST_DECLARATION | 16,
JST_SWITCHLABEL | 17,
JST_LOCALCLASS | 18,


Visual Studio 6.0 Extensibility Fundamentals

Kemp Brown
Microsoft Corporation

January 1999

Summary: Discusses extensibility in Microsoft® Visual Studio® version 6.0. (11 printed pages) Describes how to create tools, such as add-ins and wizards, to customize your development environment.

Introduction

Extensibility is the capacity to extend, or stretch, the functionality of the Microsoft Visual Studio integrated development environment (IDE). The IDE provides you with a programming interface known as the Extensibility object model, a set of powerful interfaces for customizing the environment. It allows you to hook into the IDE to create extensions known as add-ins.

There are two types of extensibility: built-in and user-designed.

  • Built-in extensibility features are already contained in the development environment. They're conveniences such as keyboard rebinding, customizable command bars, docked and tab-linked window configurations that you can name and return to at any time, new templates that you can create for the Template Wizard, and so forth.

  • User-designed extensibility features are constructed with code. They take two different forms: add-ins and wizards.
    • Add-ins are time- and labor-saving Automation tools that snap into the IDE. These tools are Microsoft® ActiveX® servers that communicate with the IDE through the Extensibility object model and are accessed through the IDTExtensibility2 interface.

    • Wizards are variations of add-ins that lead a user step-by-step through a series of actions to accomplish a complex or difficult task. Wizards are created using the IDTWizard interface.

The Extensibility object model consists of four related groups of individual code objects that control major facets of the common IDE. Each functional group enables you to create tools to customize the development environment and help you accomplish your programming tasks more quickly and easily. These groups are:

  • Window manipulation objects

  • Event objects

  • Add-in management objects

  • Project manipulation objects

Each group consists of one or more collections and objects that contribute to a particular purpose. For example, the primary function of the events object group is to provide access to events occurring in the IDE. One object in this group is the CommandBarEvents object, which allows you to respond to events occurring on menus and toolbars. Another is the BuildEvents object, which exposes events occurring at the beginning and end of solution builds.

Creating and Using Add-Ins

You can create add-ins two different ways: with the aid of a wizard or manually.

Using the Add-In Wizard

The best and easiest way to create an add-in (if you're using a common shell language such as Microsoft® Visual J++®) is to use a tool called the Add-In Wizard.

To run the Add-In Wizard

  1. Download the Visual J++ Add-In Wizard from http://msdn.microsoft.com/visualj/downloads/samples.asp. The downloaded file includes a Help file informing you where to install the wizard.

  2. Copy the file into your Windows system directory.

  3. Run regsvr32.exe on the DLL.

  4. From the Visual Studio add-ins in the New Project dialog box, select Java Add-In to launch the wizard.

The Add-In Wizard creates a basic add-in framework that you can run immediately after you finish. The wizard lets you supply a display name for the add-in (which appears in the Add-In Manager dialog box) and a description of the add-in. You can optionally choose to have the wizard generate the code to add a command to the Tools menu for invoking the add-in once it is loaded into the IDE. When the wizard is done, you will have a new project with a single item.

Note If you are using the Visual J++ development system for the Java language, and you don't install it in the default location, you must modify the add-in's project settings to correct the pathname for the alternate launch program, devenv.exe. If you launch devenv.exe as the alternate launch program for your add-in project, the second instance runs in debug mode. This allows you to step through your add-in code in the first IDE instance and watch the effects of the add-in in the second IDE instance.

Manually Creating Add-Ins

Although it's highly recommended that you use the Add-In Wizard to create your add-ins, you can also create an add-in from scratch.

To manually create an add-in in Visual J++

  1. Import the requisite extensibility type libraries msaddndr, office97, and dte:
    import com.ms.vstudio6.dte.*;
import com.ms.vstudio6.msaddndr.*;
import com.ms.office97.*;

  2. Implement the IDTExtensiblity2 interface:

    Public Class MyAddin implements IDTExtensibility2

    When you implement the IDTExtensibility2 interface in your code and register the resulting DLL after building the code, the IDE can create an add-in server and call its exposed methods.

Method name
 | Description

OnConnection
 | Called when an add-in is connected

OnDisconnection
 | Called when an add-in is disconnected

OnAddInsUpdate
 | Called when the Addins collection is updated

OnStartupComplete
 | Called when the system has completed starting

OnBeginShutdown
 | Called when the system is being shut down

    Using the Object Browser, you can view the following type libraries to examine objects in the Extensibility object model.

Type library name
 | File name
 | Description

Visual Studio 6.0 Extensibility
 | dte.olb
 | The DTE add-in object model

Microsoft Add-In Designer
 | msaddndr.olb
 | The IDTExtensibility interface

Microsoft® Office 8.0 Object Library
 | office97.olb
 | Command bars

VJExt
 | vjext.olb
 | Visual J++ project and code models

    3.

  3. Create void procedures for the previously mentioned interface methods. For example, create this procedure:

    public void OnConnection(Object VSInst, int ConnectMode, Object
AddInInst, SafeArray custom){
}

    4.

  4. In the Project Properties dialog box, select the add-in to register it.

    When you select the add-in, code is added to your add-in to identify its class ID, globally unique identifier (GUID), and type library ID. The compiler's preprocessor uses this information in conjunction with a method called @com.register to register your add-in. A registration string looks like this:

    /**
* @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000)
*/

    Note   These numbers are unique to each add-in.
    5.

  5. Once these IDs are added to your code, take note of the name listed in the Project Properties dialog box, such as MyAddin.Connect. Use this name to add a program ID to the newly generated registration string. Using the example in step 4, your code would look like this:

    /**
* @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000, progid="MyAddin.Connect")
*/

    6.

Here's how it looks when it's all put together:

import com.ms.vstudio6.dte.*;
import com.ms.vstudio6.msaddndr.*;
import com.ms.office97.*;

/**
* @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000, progid="MyAddin.Connect")
*/

public class MyAddin implements _IDTExtensibility2 {
   public void OnConnection(Object VSInst, int ConnectMode, Object AddInInst, SafeArray custom) {
   }
   public void OnDisconnection(int RemoveMode, SafeArray custom) {
   }
   public void OnAddInsUpdate(SafeArray custom) {
   }
   public void OnStartupComplete(SafeArray custom) {
   }
   public void OnBeginShutdown(SafeArray custom) {
   }
}

Note that this code serves only as a basic framework for common shell add-ins. From this point, you would add code to place the add-in as a command in a menu or command bar, create unique functionality for the add-in, and so forth.

Registering Add-Ins

Add-ins, whether created manually or with the Add-In Wizard, must be registered with Microsoft® Windows®, just as any other ActiveX server. Once registered, the IDE recognizes your add-in and lists it in the Add-In Manager. If you are using Microsoft Visual Basic®, the Add-In Designer prompts you for this information and automatically performs all required add-in registration tasks. If you manually create the add-in in Visual J++, however, you must add a public method named OnCOMRegister to reveal the add-in to the Add-In Manager. (This is done for you if you use the Add-In Wizard.) For example, this code is required:

public static void onCOMRegister (boolean reg) {
   if (reg == true) { //Register
      try {
         RegKey regKey
            = new RegKey(RegKey.getRootKey(RegKey.USER_ROOT),
            "Software\\Microsoft\\VisualStudio\\6.0\\Addins\\
            MyAddin.Connect ", RegKey.KEYOPEN_CREATE);
         regKey.setValue("Description", "Addin description goes
      here.");
         regKey.setValue("FriendlyName", "Friendly Add-In Name");
         regKey.setValue("LoadBehavior", 0);
         regKey.close();
      }
      catch (RegKeyException rke) {
         }
   }
   else {    //Unregister
      try {
         RegKey regKey
            = new RegKey(RegKey.USER_ROOT,
            "Software\\Microsoft\\VisualStudio\\6.0\\Addins",
            RegKey.KEYOPEN_WRITE);
         regKey.deleteSubKey("MyAddin.Connect");
         regKey.close();
      }
      catch (RegKeyException rke) {
      }
   }
}

Alternatively, you can manually create your own .reg file. A typical .reg file for an add-in might look like this:

REGEDIT4
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\6.0\Addins\
   ProjectName.Classname
"Description"="A description of the add-in."
"FriendlyName"="My new addin"
"LoadBehavior"=dword:00000000

For information about creating add-ins in Visual Basic, see "Using Add-In Designer" in the Visual Basic documentation. Also see the sample add-in written in Java in the Visual J++ Samples directory.

Creating and Using Wizards

Wizards are a type of add-in that leads a user through a sequence of actions, generates projects and project items, and then goes away. They implement the IDTWizard interface, which has only a single method, Execute.

Although they can be constructed any way you like, Microsoft implements its wizards in a specific manner. A Microsoft wizard consists of a number of frames. Each frame contains an image in the upper-left corner, a label description to the right of the image that may also contain instructions, and an optional area near the bottom in which other controls can be placed, as in the following example.

The process of creating a wizard is very similar to creating a regular add-in.

How To Create a Wizard

  1. Import the requisite extensibility type libraries msaddndr, office97, and dte:

    import com.ms.office97.*
import com.ms.vstudio6.msaddndr.*
import com.ms.vstudio6.dte.*

    2.

  2. Implement the IDTWizard interface:

    Public Class MyAddin implements IDTWizard

    When you create a wizard, you gain access to the Wizard Extensibility model using an interface called IDTWizard. When you implement the IDTWizard interface in your code and create a .vsz file in one of the appropriate directories, the IDE can create a wizard and call the Execute method.
    3.

  3. Create a void procedure for the Execute method. For example, create this procedure:

    public void Execute(int RemoveMode, SafeArray custom)
  {
   }

    4.

  4. In the Project Properties dialog box, select the wizard to register it.

    When you select the wizard, code is added to your add-in to identify its class ID, globally unique identifier (GUID), and type library ID. The compiler's preprocessor uses this information in conjunction with a method called @com.register to register your wizard. The registration string looks like this:

    /**
* @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000)
*/

    Note   These numbers are unique to each wizard. Also, you don't need to register your wizard with the IDE with the onCOMRegister procedure as you do with ordinary add-ins.
    5.

  5. Once these IDs are added to your code, take note of the name listed in the Project Properties dialog box, such as MyWizard.Connect. Use this name to add a program ID to the newly generated registration string. Using the example in step 4, your code would look like this:

    /**
* @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000, progid="MyWizard.Connect")
*/

    Here's how it looks when it's all put together:

    import com.ms.office97.*
import com.ms.vstudio6.msaddndr.*
import com.ms.vstudio6.dte.*

/**
* @com.register (clsid=710B1AE4-9E3A-1101-883A-B07C0CC10000,
   typelib=710B1AE3-9E3A-1101-883A-B07C0CC10000,
   progid="MyWizard.Connect")
*/

public class MyAddin implements _IDTWizard {
   public void Execute(Object VSInst, int ConnectMode, Object
      AddInInst, SafeArray custom) {
   }
}

    6.

  6. Next, to expose the wizard in the IDE, create a .vsz file. A .vsz file is a small text file that identifies the wizard, where its executable is located, plus any parameters the wizard requires. Here is a sample of a .vsz file:

    VSWizard 6.0
Wizard=VIDWizard.CblankSiteWizard OR GUID
Param=test1
Param=test2

    7.

  7. Place the .vsz file in a directory that contains new files or projects.

    The wizard then appears in the Add Item and Add Project dialog boxes as a new item that can be added to a solution. You can also refer to the .vsz file in a .vsdir file to control how and where it displays in the Add Item and Add Project dialog boxes.
    8.

Using Wizards in a Solution

You can place files in the projects and items directories and use them in a Visual Studio solution. To customize which items are displayed in the Add Project and Add Item dialog boxes, as well as the order in which they are displayed, you can use .vsdir files. (Note, however, that .vsdir files are not part of the Automation model.)

The IDE looks for files with a .vsdir extension in the directories that contain new items and projects. .Vsdir files provide a sort order for items displayed in the Add Project and Add Item dialog boxes. There can be multiple .vsdir files at each level in the directories that contain new projects and items. The IDE reads all of them at a given level and builds an overall order for displaying items. Items not listed in .vsdir files appear after all items listed in a .vsdir file. For more information about how to construct .vsdir files, see the section "Creating .Vsdir Files" later in this article.

While regular add-ins cannot be used in the projects and items directories, you can, however, place wizards in these directories and have them activate if they have an associated .vsz file. For information about creating .vsz files, see the next section.

The IDE also provides a Template Wizard that can be used to prompt users for information and merge that information with template sources to produce new project items. Users can add a new template to the Add Item dialog box by creating a new .vsz file that refers to the Template Wizard and the sources for the template. For more information on this, see "Creating Custom Templates" in the Visual Studio documentation.

Creating .Vsz Files

A .vsz file represents items that appear in the Add Item and New Project dialog boxes as new items and projects that can be added to your solution. The .vsz file contains information that identifies a wizard and provides custom information for it. The custom information allows you to invoke the same wizard with different data from different .vsz files. This allows a single wizard to perform seemingly different functions.

The following shows the format of a typical .vsz file:

  VSVersion=6.0
  ClassName=MyProgID.Info
  Param=c:\users\fizzle.java
  Param="another custom parameter"

Part | Meaning
VSVersion | Identifies the wizard interface the wizard will be expected to implement.
Classname | The ProgID of the wizard.
Param | Custom parameters that get passed to the wizard upon execution. You can have any number of custom parameters. Each parameter is contained on a separate line. The quotes shown in the preceding example are optional.

Creating .Vsdir Files

You can optionally create a .vsdir file for your wizard, or modify an existing file to contain information about the wizard. A .vsdir file is a text file that is used to provide information to the Add Item and New Project dialog boxes. The .vsdir file provides information about the items these dialog boxes display, such as the sort order and the icon to display for each item.

You can have multiple .vsdir files in a single directory; however, one .vsdir file is generally used to contain information for multiple wizards, folders, or templates. An example of a typical .vsdir file is shown here:

Form.java|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36201|10|#36202|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#253|0|Form
Control.java|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36203|20|#36204|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#258|0|Control
DATAFORM.VSZ|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36220|40|#36221|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#262|0|DataForm
Form|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36056|10
Class|{AECC23E0-8AA3-11d0-B606-00A0C922E851}|#36054|20

.Vsdir files contain a single record per file or folder template, with fields separated by a pipe (|) character. Any nonrequired field for which there is no meaningful data should contain a zero (0) as a placeholder. The following fields can be specified for a given record.

Field | Meaning
RelPathName | Name of the .vsz file for the wizard.
{clsidPackage} | Optional GUID representing a product (for example, Visual J++) that has a DLL containing localized resources.
LocalizedName | Optional name of the wizard as it will appear in the Add Item dialog box—can be a string or a resource identifier of the form "#ResID."
SortPriority | An integer representing the relative priority of the wizard, with 1 being highest. Determines the sort order of items.
Description | Localizable description of the template or wizard as it will appear in the Add Item dialog box—can be a string or a resource identifier of the form "#ResID."
DLLPath or {clsidPackage} | Used to load an icon for the wizard from a .dll or .exe file. Specify either the full path to the .dll or .exe, or a GUID of a product that has a .dll file containing localized resources.
IconResourceId | Optional resource identifier within the .dll file that determines the icon to display. If no icon is defined, the shell substitutes the default icon for a file with the same extension as the item.
Flags | A group of flags that disable or enable the Name and Location fields on the Add Item dialog box. The flags can have the following values: Space (" "): enable file name 8192: disable file name
SuggestedBaseName | The default name for the wizard—either a string or a resource identifier of the form "#ResID." If necessary, the shell appends the base value with an integer to make the name unique. For example, the shell might change MyFile.asp to MyFile1.asp. This default name is shown in the Name field in the dialog box.

Taking Advantage of Extensibility

There are many ways you can use add-ins and wizards to enhance, extend, and automate the IDE:

  • Manipulate environment-wide properties such as those found in the Options dialog box from the Tools menu.

    •

  • Navigate the solution and project structure by adding and removing projects and project items, opening project items in one of several views (for example, designer or source), modifying project items, and so on.

    •

  • Add and remove commands and get events when commands fire.

    •

  • Access and control all the windows in the environment: close them, dock them, hide them, set the focus, and even create new tool windows that are dockable and behave like built-in tool windows. You can also access the text buffer for source editor windows.

    •

  • Sink events that occur when solutions are opened and closed, builds begin and end, projects and project items are added or removed, or when the selection changes.

    •

  • Access the Visual J++ code model from any Visual J++ project's .java item to navigate and manipulate the code in that file. You can also access the code model from the root of the add-in object model to access a particular package, class, or file by name.

    •

  • With .java files, access objects from the code model object for the package into which the code is compiled, the collection of imports at the top of the file, and a collection of classes in the file.

    •

  • Add or remove imports.

    •

  • With class objects, access or modify a collection of interfaces implemented by the class, a collection of members, a collection of superclasses, a collection of subclasses, and the modifiers for the class.

    •

  • With members, distinguish fields from methods, get type information for fields and signature information for methods, return to the class object, get the initializer expression for a field, access the modifiers, access the statements in methods, and add and remove members, as well as modify these properties.

    •

  • With statements, query their type (for example, if, for, sync, and so on) and add or remove statements from blocks of code.

    •

  • For source elements (classes and members), access their javadoc comments as an object. This object lets you get the comment as a synopsis, a short description, or the entire text. You can index the object by name for various tags. Some standard tags that are likely to occur repeatedly in a single comment are available also in special collections. For example, you can index @param tags by the name of the parameter to get the text of the tag.
    •

These are just a few ideas. Add-ins give you the freedom to create custom programming solutions to save you time and help you become more productive. Just think of the possibilities!


Unifying the Programming Model for Windows and the Internet

Every day, more organizations are turning to client/server solutions based on Internet protocols running on the Microsoft® Windows® platform. As developers we've all been finding our way around this new application architecture, often frantically searching for ways to more easily bridge the assorted technologies. It's easy to get excited about the new development opportunities fostered by the marriage of the Internet and client/server computing. However, when implementing solutions across these different programming disciplines (with varying degrees of maturity) it is just as easy to get frustrated while productivity suffers.

In response, Microsoft is creating the Windows Foundation Classes (WFC) to lower the bar of entry for developers into this rich new environment. WFC is an object-oriented framework that encapsulates, simplifies, and unifies the Microsoft® Win32® and Dynamic HTML programming models. WFC is specifically aimed at developers that want to take full advantage of the spectrum of features essential to capitalize on both Windows and the Internet and therefore create winning solutions while cutting development time.

Developers want to create and deploy:

  • Applications that adhere to industry and world wide standards for networking, data sharing, user interface that allow their software to interact with software from all around the world from any vendor.

  • Web server applications that response to HTTP URL requests that in turn do database connectivity. These applications return HTML visible by standards based, broad reaching Web Browsers onto platforms ranging from Microsoft® Windows® CE handheld devices to Microsoft® WebTV® to the Macintosh® to legacy Windows 16-bit.

  • Specialized business components that transact a specific line of business on high volume servers

  • Client front-ends that take full advantage of the richness of the latest DHTML features that may include rich encapsulations of the most commonly used controls today (trees, calendars, grids, etc).

  • Applications that are triggered from server events that create and maintain static Web sites.

  • Secure, robust applications that are easy and low cost to create, deploy and administer.

  • Highly powered, high performance, complex, feature rich client front-ends that remote DCOM invocation over high-bandwidth protocols to remote specialized objects.

  • Applications that have clients that behave just as well when connected as when offline -- reconciling changes made to data upon reconnection.

  • Applications that bind to heterogeneous data formats that are found locally and remote.

  • Some combination of all of the above!

With WFC, developers can now build these applications without sacrificing the productivity they have come to expect from traditional class libraries and tools.

Common Component Model

WFC accomplishes this by boiling everything down to a common, strong component model that emphasizes a relatively small set of very consistent, easy to understand rules about how components are created, how they interact, how to extend them and how to use and reuse them.

This base-line architecture was then applied across the key features of the platform to form a
uniform programming model for:

  • User interface based on either DHTML and Win32

  • Operations performed on the server or clients with high speed connection, low speed or disconnected.

  • Operating from a Microsoft® MS-DOS® command line

  • Databinding to SQL, Microsoft® Access®, Oracle, Microsoft® XML, tabular data all local or remote

What follows is an exploration of what developers can expect to find when targeting WFC in their client/server solutions.

Common UI Programming Model for DHTML and Win32

The creators of WFC understand that there are different tools and runtimes that are applicable for different situations. However, they also believe that developers are more productive if they can learn one set of coding principles and practices and apply it while targeting different cases. Take the following two code snippets:

Win32-based UI

import com.ms.wfc.ui.*;
{
    Form   form1   = new Form();
    Button button1 = new Button();
    button1.setText("OK");
    button1.setPosition( 10, 10 );
    form1.add(button1);
}

Dynamic HTML-based UI

import com.ms.wfc.html.*;
{
    DhForm  form1   = new DhForm();
    DhButton button1 = new DhButton();
    button1.setText("OK");
    button1.setPosition( 10, 10 );
    form1.add(button1);
}

While both generate interactive user interface, the code on the left creates UI that is built directly on top of Win32's USER/GDI implementations while the code on the right generates HTML compliant with the Worldwide Web Consortium (W3C) specification. What should be obvious is that the programming model is the same for both cases. In both there are components with empty constructors, properties and methods that are the same and are addressed the same way.

What is not obvious in the two code snippets above is that while both will execute on the client, the code on the right can also execute on a server, sending the result of the generated HTML to an unknown client. (More on this below.)

WFC also takes full advantage of J/Direct technology and makes it possible for developers to write against the WFC Graphics objects on the server for Web applications or on Win32 client machines. Taking advantage of the latter results in graphic rendering and reaction times the likes of which Java programmers have never seen before.

Win32-based IO

Bitmap bitmap;
{
  bitmap = new Bitmap(100,50);
  Graphics g = bitmap.getGraphics();
  Font f =
    new Font("Haettenschweiler",12);
  g.setFont(f);
  g.drawString("Cool!");
}

public void onPaint(PaintEvent pe)
{
  // CLIENT:
  // Write the image to the screen:

  pe.graphics.drawImage(bitmap);
}

Dynamic HTML-based UI

Bitmap bitmap;

{
  bitmap = new Bitmap(100,50);
  Graphics g = bitmap.getGraphics();
  Font f =
    new Font("Haettenschweiler",12);
  g.setFont(f);
  g.drawString("Cool!");
}

public void initForm()
{
  // SERVER:
  // Send the image to the client:

  String fname = "cool.bmp";
  File f = new File();
  f.create(fname);
  bitmap.save(f);
  f.close();
  DhImage img1 = new DhImage(fname);
  getResponse().write(img1);
}

Note how the code to create a bitmap on the Win32 client on the left is the exact same code that runs on the Web server on the left. The only difference, again, is where the image is rendered -- immediately to the screen in the case of the client or sent to the client's HTML browser.

To take full advantage of Microsoft® Internet Explorer 4.0, developers using WFC can very easily and intuitively mix and match Win32, COM and HTML components all in the same UI and coding namespace. With only a few lines of code developers can put WFC authored Win32/COM controls directly into the HTML page. It is just as easy to have Win32 dialogs that come up over the browser and have all of those seamlessly communicate with the intrinsic HTML on the page.

import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;

{
  // Bind to a text span called "text1"
  // in the HTML document:

  DhText t = new DhText();

  setBoundElements( new DhElement[] {
                  t.setBindID("text1") } );

  // Hook it's 'onclick'

  t.addOnClickHanlder(
           new EventHandler(this.onText1Click) );
}

public void onText1Click(Object s, Event e )
{
  // In response to click on text span,
  // show a Win32 dialog on top of the browser

  Form1 form1 = new Form1();
  form1.showDialog();
}

Client/Server Unification

With the advent of Dynamic HTML (DHTML) and Microsoft Internet Explorer (MSIE) 4.0's implementation of that specification, developers have much more flexibility. When targeting Microsoft Internet Explorer 4.0 on the client, many of the decisions about when and where code has to run become more about where the code should run to build the best solution.

Part of the way WFC accomplishes this symmetry is by leveraging the Component Object Model (COM) on Internet Information Server (IIS) as well as Microsoft Internet Explorer. This means developers using WFC can write a single module of that will run "behind" either an Active Server Page or an HTML page running in Microsoft Internet Explorer 4.0 with simple runtime checks to validate which host they are running behind in case they need to know.

Common Event Model

Another important way WFC leverages Microsoft Internet Explorer is in event handling. By unifying the programming model for handling and firing events, developers that target Microsoft Internet Explorer's Win32 browser can apply the same code to both Win32 based UI as well as HTML:

Win32-based UI

import com.ms.wfc.ui.*;

{
   // This watches for WM_LBUTTONDOWN

   button1.addOnClick(
    new EventHandler(this.onClick));
}

public void onClick(Object s, Event e )
{
}

Dynamic HTML-based UI

import com.ms.wfc.html.*;

{
   // this watches for <INPUT ONCLICK=

   button1.addOnClick(
    new EventHandler(this.onClick));
}

public void onClick(Object s, Event e )
{
}

Event handlers can be mixed and matched at will including pointing events coming from DHTML and Win32 at the same listener (n to 1) or having multiple listeners to a single event source embedded anywhere in the UI (1 to n). Borrowing a page from Microsoft® JScript® and Microsoft® Visual Basic® Scripting Edition (VBScript), the WFC event model is not interface based, it is method based. That means any code in any class becomes a listener simply by implementing a method with the proper argument signature and letting the source know they want to listen in as evidenced by the code snippets above.

Cross Discipline Productivity

Currently developers providing real world client/server solutions need to master at least three coding disciplines just to display user interface:

  • Typography (HTML, CSS, etc.)

  • Scripting (JavaScript or VBScript or both, DHTML, DOM, etc.)

  • Graphics/Controls Libraries (AWT, JFC, etc.)

WFC eliminates the need to repatriate professional, code-writing developers into foreign disciplines not directly related to the types of solutions they are commissioned to create and maintain.

While it is very easy to create great looking HTML content by writing code with WFC, Microsoft encourages developers to pre-author HTML in a Web authoring tool (e.g. Microsoft Front Page). Microsoft also recommends that the HTML elements that are to be addressed at runtime by WFC be marked as such using attributes. This makes it much faster to hook UI events to the elements at the client or do dynamic element substitution on the server.

Single Data-Binding Mechanism

At the core of data binding in WFC is the Microsoft Universal Data Access initiative. This is very rich and far-reaching architecture that gets developers access to most of the world's data. At the programming level, however, WFC encapsulates the complexity while retaining all the functionality of the underlying data system.

With just a few lines of code a WFC Grid control and an HTML single record details form (e.g. "First Name", "Last Name", etc.) can be placed on the same HTML page. With a few more lines of code, both the master and the details form can be bound to the exact same data source such that as records are navigated in the grid are reflected in the HTML. For clients that do not have Microsoft Internet Explorer or WFC installed, this same application, largely unchanged can be deployed and run from a server.

WFC make this possible by abstracting the data binding programming model for client and server user interface and being intelligent about when to apply client side data-binding vs. server side data-binding. While other tools attempt similar functionality, it is it's robust component architecture and the fact that it is an open well-defined class library that makes WFC's approach much more amenable to professional developers.

Summary

There are many topics to cover and much more detail that can and will follow, but for now it should be clear what the mission of WFC is:

To be a framework for quickly writing and maintaining robust client/server applications on Windows using open Internet standards.

To accomplish this WFC introduces a compelling component model that is the basis for a unified programming model for classic Win32 based programming as well as Internet standards such as HTML and HTTP.


WFC Programming Concepts

Windows Foundation Classes for Java (WFC) provides a framework of Java packages that support components targeted for the Microsoft® Windows® operating system and the Dynamic HTML object model. WFC is tightly integrated with the Microsoft® Visual J++® development environment and provides a full set of Windows controls written in Java. Building a Java application for Windows is made much easier by this tight integration and the support of features such as IntelliSense, the Forms Designer, the Application Wizard, and the Object Browser. While these Visual J++ features put you well on your way to creating applications, you'll probably want to understand the structure and logic behind the packages and classes that make up WFC.

The purpose of this section is to provide a conceptual framework for the WFC packages and classes and to explain some of the fundamental WFC models. Many of the packages exist as infrastructure for the component model and can be ignored by developers focusing on using the WFC controls. Other packages are most easily accessed from the Visual J++ Forms Designer. When you start exploring the WFC library, you'll want to know which packages and classes are important for your particular application.

The following topics are covered in this section:

  • WFC Packages provides a high-level overview of the main packages that make up WFC.

  • Working with WFC Visual Components describes the controls, forms, and graphical objects in WFC, which include the following:
    • Windows Visual Components

    • Dynamic HTML Visual Components
  • Handling Events in WFC describes the use of delegates for handling events.

  • Localizing Your Application describes the Visual J++ and WFC support and methodology for localizing your project in various languages.

  • Using WFC Application Services describes some of the core application features and includes the following subtopics:
    • Starting and Quitting an Application

    • Handling Application Events

    • Accessing System Information

    • Performing Clipboard and Drag-and-Drop Operations
  • Using Java Threads with WFC describes the threading model for WFC and includes the following subtopics:
    • Mixing Java and Microsoft® Win32® Threading Models

    • Creating and Exiting a Thread

    • Using Thread Storage

    • Working with Thread Exceptions


WFC Packages

The basic foundation blocks of WFC are Windows and Dynamic HTML. WFC is rooted firmly in the Win32 Windows programming model, enabling you to use Java to write Windows-based applications that take advantage of Windows user-interface controls, events, and system services. WFC is also rooted in the Dynamic HTML object model, which enables you to create both client and server HTML pages that use the power of Dynamic HTML directly from Java.

At the heart of these technologies are native dynamic-link libraries (DLLs) that provide the core API of the WFC infrastructure. These libraries are made available to the Java language thanks to two different technologies: the JActiveX tool and Microsoft® J/Direct™. If the DLL represents a COM/Microsoft® ActiveX® component, JActiveX creates wrapper classes that map the COM objects to Java objects. If the DLL is not COM-based, J/Direct is used to call directly into the DLL and to marshal the data types between Java and the native language of the DLL (such as C or C++). Both these technologies take advantage of the built-in support and synergy of the JVC compiler and the Microsoft virtual machine.

This is mostly important to know because several WFC packages are composed entirely of either COM wrapper classes (produced by JActiveX) or J/Direct classes. These classes have methods that map directly to the underlying API; they are not documented in the WFC Reference because they are not typically called directly. However, they are discussed as support classes for other packages.

Not including the native API support packages, there are seven main packages in WFC.

Package | Description
com.ms.wfc.app | Base classes that encapsulate Windows application operations. The Visual J++ form component template uses the services of this class. In addition to the basic Windows message handling structure, there is support for Windows features such as the Clipboard, the registry, threads, window handles, system information, and so on.
com.ms.wfc.core | Base classes for the component model. This package includes support for containers, events, exceptions, properties, and the infrastructure for interoperating with features in Visual J++ such as the Forms Designer.
com.ms.wfc.data | Active Data Objects (ADO) Java classes that enable data access and data binding. This package also includes com.ms.wfc.data.ui, the package that provides the base classes for the data-bound controls in WFC.
com.ms.wfc.html | Classes used to implement Dynamic HTML in Java. These classes provide both client- and server-side support.
com.ms.wfc.io | Classes used to access data streams, implementing a complete package for reading and writing serial streams, for file access, and for mapping between differing types of data streams.
com.ms.wfc.ui | Core classes for the controls that ship with WFC. These classes also provide access to the Windows Graphics API.
com.ms.wfc.util | Utility classes for various forms of sorting, implementing hash tables, and so on.

The following are the core native API support classes in WFC.

Package | Description
com.ms.wfc.ax | Provides Java wrapper classes for the ActiveX interfaces.
com.ms.wfc.html.om | Provides Java wrapper classes for the Dynamic HTML object model.
com.ms.wfc.ole32 | Provides Java wrapper classes for OLE services.
com.ms.wfc.win32 | Provides Java wrapper classes for Win32 API.


Working with the Visual Components of WFC

While there are some differences in the WFC visual components of Win32 and Dynamic HTML (DHTML) applications, there are many more similarities. These similarities enable the WFC component model to easily serve both types of applications. For example, both the Win32 and DHTML models include basic control types, such as edit boxes, check boxes, buttons, radio buttons, and combo boxes. For the most commonly used controls in Win32, there is usually a DHTML control with the same name prefaced by the letters "Dh" (for example, the com.ms.wfc.ui.Edit class has a corresponding com.ms.wfc.html.DhEdit class). Fonts, color constants, and most event types are also shared between the Win32 and DHTML models.

Of course, there are also components that are specific to each model, such as tables for DHTML or list view controls for Win32 components.

Probably the most notable difference between the WFC Win32 and DHTML components is that the DHTML components are not available from the Forms Designer. This means you must create, add, and modify DHTML elements in the code editor; however, the underlying Java code looks very similar in both models.

Windows Visual Components

The WFC framework is rooted in its visual components: WFC is integrated with Visual J++, a visual development tool, and sits on top of the Win32 API for the Windows operating system. The main package in WFC that supports the visible components is com.ms.wfc.ui and the base class for most visual elements in WFC is com.ms.wfc.ui.Control. Most of the WFC controls extend this class, including the Form class, which is the visual container for controls.

The Control Class

The Control class contains all the essential base properties, methods, and logic for manipulating a Win32 window. These methods can be categorized as follows:

  • Methods for setting and retrieving the control's properties, such as the size of the display and client rectangles, the foreground color, background color, associated brush, cursor, text, font, position, and so on. These methods have names that start with set or get (for example, setBrush and getBrush).

  • Event methods. For every event that a control generates, there are three methods implemented in the control. For example, for the move event, there is an onMove method, which triggers the event, an addOnMove method, which assigns an event handler to the move event, and a removeOnMove method, which removes the event handler. The Control object handles most basic control events.

  • Methods dealing with parent/child relationships of controls. For example, the add method adds a child control, and assignParent and getParent assign and retrieve the parent control. There are also methods for handling arrays of child controls.

  • Methods affecting the layout, z-order, painting, and input focus of the control, such as bringToFront, sendToBack, updateZOrder, performLayout, focus, show, hide, update, invalidate, createGraphics, and createWindowGraphics.

  • Low level event-processing methods. At the Win32 level, windows receive messages from the system. In the Control class, every input message to a control has a protected process method (for example, processCmdKey, processCmdKeyEvent, or processDialogKey). These methods are important only if a control extending this base class wants to override them to do special processing.

  • Methods relating to window handles, messages, and thread invocation. These are available for programmers experienced with the Win32 programming model. For example, createHandle, destroyHandle, getRecreatingHandle, fromHandle, fromChildHandle all deal with window handles, and sendMessage and reflectMessage allow the control access to the underlying window messages. For descriptions of the thread invocation methods, see Using Java Threads with WFC.

The Control class extends com.ms.wfc.core.Component class, which is the base class for all WFC components.

Using Forms

A form is the main visual element of an application or a custom dialog box associated with an application. The com.ms.wfc.ui.Form class serves as the foundation for forms in WFC.

The Visual J++ Forms Designer starts with the Visual J++ form template, which provides a class extending com.ms.wfc.ui.Form, and helps you to set properties on the form and add controls to it. The Form-derived class adds a method called main, not found in Form. The form is run when the com.ms.wfc.app.Application.run method is called from main and passed a new instance of the Form-based class (this code already exists in the template). For more information, see "Starting and Quitting an Application" in Using WFC Application Services. A Form-based class used as a modal dialog box can be run by calling the Form.showDialog method (showDialog also runs modal dialog boxes that are based on the com.ms.wfc.ui.CommonDialog box class). A Form-based modeless custom dialog box can be opened by calling the form's show method, which makes the form visible.

The Form class extends com.ms.wfc.ui.Control, so it has all the Control methods plus many of its own for handling its role as a container for controls and as a window. These include methods used to:

  • Add and remove handlers for the active, closed, closing, deactivate, inputLangChange, inputLangChangeRequest, MDIChildActivate, menuComplete, menuStart, and ownedForm events.

  • Set the form's window properties, for example, to set the form's initial maximized or visible state, start position and border style; or to determine whether the form has an automatic scroll bar, a control box, or a minimize box, and whether the form's icon is in the task bar.

  • Determine the relationship of menus, controls, or other forms placed on the form, such as setting the main menu, arranging controls, supporting multiple document interface (MDI) forms, or determining whether the form receives all the control's key events.

  • Set properties when the form is used as a modal dialog box and to run and retrieve results from a dialog box when the form is an application. This includes methods to set the Accept, Cancel, and Help buttons on a dialog box, to start a dialog box, and to set and retrieve the dialog result values returned by a modal dialog box.

This list is not comprehensive but provides a general idea of what a form is. One other class in the com.ms.wfc.ui package extends Form, and that is the UserControl class. UserControl is a class for creating your own composite Form-based controls that you can install in the Toolbox.

Overview of WFC Controls

All visible WFC controls reside in the com.ms.wfc.ui package. With over 240 classes in this package, it can be difficult to immediately determine which classes you want to use. Fortunately, the classes fall into several major categories, as follows:

  • Classes that are controls in the Visual J++ Toolbox and directly extend the com.ms.wfc.ui.Control class or ultimately derive from the Control class.

  • Classes that are controls in the Visual J++ Toolbox and use CommonDialog as a base class (this includes the CommonDialog class itself). CommonDialog wraps the Win32 CommonDialog API.

  • Classes that contain constant values used by controls. These classes all extend com.ms.wfc.core.Enum.

  • Classes that represent events and extend com.ms.wfc.core.Event or are event handler classes (delegates).

  • Classes that represent intrinsic Windows graphical objects such as brushes, bitmaps, colors, cursors, fonts, pens, palettes, icons, regions, and images. For pointers to detailed information on using these objects, see the next section, "Accessing Graphical Services."

  • Classes that, like Control, extend com.ms.wfc.core.Component but do not require the visual run time overhead of Control. Some examples are ColumnHeader, Menu (and MainMenu, ContextMenu, and MenuItem, which extend it), RebarBand, StatusBarPanel, ToolTip, and ImageList (and ImageListStreamer, which extends it).

  • Miscellaneous classes that wrap other Windows interfaces; these include Help, which wraps the Windows Help engine, HTMLControl, which wraps a browser, and MessageBox, which wraps the Windows message box, among others.

The following com.ms.wfc.ui classes directly extend Control. Controls that extend these classes are listed in the description.

Class | Description
Animation | Encapsulates a Windows animation control, a rectangular control that plays an Audio-Video Interleaved (AVI) animation file.
AxHost | Wraps an ActiveX control and exposes the control as a WFC control.
Button | Encapsulates a Windows button control.
Checkbox | Encapsulates a Windows check box control, which is a labeled box that is checked or unchecked to select or clear an option.
ComboBox | Encapsulates the Windows combo box control.
DateTimePicker | Encapsulates a Windows date and time picker control, which allows users to specify date and time information.
Edit | Encapsulates a Windows edit control, which is a rectangular control that the user can enter text into.
Form | Represents the basic top-level window.
GroupBox | Encapsulates a group box control, which is a rectangle that contains other controls.
Label | Encapsulates the Windows label control, which displays a string of text that the user cannot edit.
ListBox | Encapsulates the Windows list box control, which displays a list from which the user can select one or more items. ListItem is used with this class. The CheckedListBox control extends this class.
ListView | Encapsulates the Windows list view control, which displays a collection of items, each consisting of an icon (from an image list) and a label.
MDIClient | Represents a window that contains MDI child windows.
MonthCalendar | Encapsulates a Windows month calendar control, which provides a simple calendar interface from which the user can select a date.
Panel | Represents a container you can use to parent other controls visually. The TabPage class extends Panel.
PictureBox | Encapsulates a Windows PictureBox control used to contain bitmaps.
ProgressBar | Encapsulates the Windows ProgressBar control, which dynamically tracks the progress of an operation by moving a bar.
RadioButton | Encapsulates a Windows radio button (or option button) control, which displays an option that can be selected or cleared.
Rebar | Encapsulates a rebar control, which contains other controls within moveable, resizable bands. RebarBand is used with this class.
RichEdit | Encapsulates a Windows RichEdit control.
ScrollBar | Represents the base class for scroll bar controls. HScrollBar and VScrollBar extend ScrollBar.
Splitter | Encapsulates the splitter control, which allows the user to resize docked controls at run time.
StatusBar | Encapsulates the Windows status bar control. StatusBarPanel is used with this class.
TabBase | Defines the base class that contains common functionality for Tab classes controls. TabControl (which uses TabPage) and TabStrip (which uses TabItem) extend this class.
ToolBar | Encapsulates the ToolBar custom control. ToolbarButton is used with this class.
TrackBar | Encapsulates a Windows trackbar control (also known as a slider control), which contains a slider for selecting a value in a range.
TreeView | Encapsulates a Windows tree view control. TreeNode is used with this class.
UpDown | Encapsulates the up-down control (sometimes called a spinner control).

Accessing Graphical Services

In the WFC environment, applications perform graphical operations by using the Graphics object, which encapsulates the native drawing capabilities of the Windows operating system. This object provides flexible support for the most commonly used drawing operations, including displaying images and icons and drawing lines, polygons, and text.

The Graphics object performs its work by wrapping a Windows device context, a system data structure that defines system graphical objects, their associated attributes, and the graphic modes affecting output to a device. Because you can retrieve the device context that underlies a Graphics object, you can use the Graphics object seamlessly with native Win32 drawing routines.

All WFC objects that extend the Control object support creating a Graphics object through the createGraphics method. In addition, all objects that extend the Image object, such as Bitmap, Icon, and Metafile, support the creation and retrieval of their associated Graphics object through the getGraphics method.

For more information on how to use this object, see Graphical Services.

Dynamic HTML Visual Components

Dynamic HTML elements make up the second set of visual components in WFC. The controls in com.ms.wfc.html are based on the Dynamic HTML object model. The classes in com.ms.wfc.html are used to create new elements and also to bind to existing elements on an HTML page. These components can be created and manipulated on a client browser or on a server, which sends them to a client browser. This object model exists on several platforms. Therefore, it is not fundamentally rooted in Win32, although user interface controls tend to be similar because the standard set of buttons, list boxes, radio buttons, and so on, are present on both.

Both sets of WFC controls (Win32 and Dynamic HTML) have a similar base because they both ultimately derive from com.ms.wfc.core.Component. Components are elements that can be hosted in a container and support the IComponent interface, which has methods for siting the component. How components and containers are hooked up is of little concern to most programmers using WFC; however, because elements from both com.ms.wfc.html and com.ms.wfc.ui are based on components, they exhibit similar characteristics. For example, all components are added to their parent containers using the add method.

For a better understanding of how to use the com.ms.wfc.html package, see Programming Dynamic HTML in Java.


Handling Events in WFC

The Control base class and classes that extend it, such as buttons and edit boxes, expose standard Windows events such as click, keyPress, mouseMove, dragDrop, and others. You can work with an event in your application using delegates. You do not have to understand delegates in great detail to write event handlers in an application. However, understanding how to create and use delegates is useful if you are building controls, working with other applications that trigger events, or are using threads with WFC components. It is also interesting if you want to understand the details of the Java code created by the Forms Designer. This section provides some background on delegates and then addresses the practical aspects of handling events.

What is a delegate? A delegate declaration defines a class that extends com.ms.lang.Delegate. The JVC compiler also recognizes delegate as a keyword, providing a shortcut for creating a delegate-based class. A delegate instance can call a method on an object and pass data to that method. Most importantly, the delegate is isolated from the object it refers to and needs to know nothing about it. Therefore, it is ideal for "anonymous invocation." In other languages, this functionality is implemented as a function pointer. However, unlike function pointers, delegates are object-oriented, type-safe, and secure.

In WFC, delegates are most often used to bind events to handler methods, such as a click event on a button control to a handler method in your class. When the event occurs, the control invokes the delegate, passing it any event information. The delegate, in turn, calls the registered handler method and passes the event data. You can also use delegates to bind one event to more than one method (called multicasting); when the event occurs, each delegate in the list is called in the order in which they were added. Conversely, delegates from different events can be assigned the same handler method (for example, a toolbar button and a menu item can both call the same handler).

To work with events in your application, you use a delegate to register for notification when that event occurs for a specific control. To register, call the addOn<event> method of a control, where <event> is the name of the event you want to handle. For example, to register for the click event of a button, you call the button object's addOnClick method.

The addOn<event> method takes as a parameter an instance of a delegate, typically an existing WFC delegate that is associated with specific event data. In the addOn<event> call, the delegate instance is created with a reference to the method you want to bind the event to. The following example shows how you would bind the event handler "btnOK_Click" (in the current class) to the click event of a button called btnOK.

   Button btnOK = new Button();
   btnOk.addOnMouseClick( new EventHandler( this.btnOK_Click ));

For most events, you can create and pass an instance of the generic EventHandler delegate, which passes a generic Event object. However, some events use special event handler classes when they include extra, event-specific information. For example, mouse movement events typically include information such as the mouse cursor location. To get this type of information, you create and pass an instance of the MouseEventHandler class, which passes a MouseEvent object to the handler. Keyboard events require the KeyEventHandler to get information about the status of SHIFT keys, and so on (this handler passes a KeyEvent object).

All WFC event handler delegate classes extend com.ms.lang.Delegate. Most of them are in the com.ms.wfc.ui package with names that end in EventHandler. All WFC events extend com.ms.wfc.core.Event, have names that end in Event, and can be found in the com.ms.wfc.ui package.

Tip In the Forms Designer, you can use Events view in the Properties window to bind an event to a specific method. The Forms Designer then creates the appropriate addOn<event> method and the skeleton handler for you.

When the delegate calls your handler, it passes two parameters. The first parameter is a reference to the object that originated the event. The second is an event object that can contain information about the event. A handler for the delegate from the preceding example might look like this:

   private void btnOK_Click(Object source, Event e)  {
      if (source instanceof Button) {
         String buttonName = ((Button)source).getText();
         MessageBox.show("You clicked button " + buttonName);
      }
}

If you used the generic EventHandler class to bind to your method, the Event object in your handler will not contain any interesting information. But if extra information is available for the event, you can extract it from the specific event object. The following is what the delegate and handler might look like for a mouse movement event. The MouseEvent object exposes properties that allow you to get the mouse position.

   // This is the request for notification
   Button btnTest = new Button();
   // Note that the addOn<event> method uses the MouseEventHandler class
   btnTest.addOnMouseMove( new MouseEventHandler(this.btnTestMouseMove));

   // This is the handler for the mouse movement event
   private void btnTestMouseMove(Object source, MouseEvent e){
      edit1.setText( e.x + ", " + e.y);
   }

If you want to handle events for multiple controls or multiple events for the same control, you request a separate notification for each control/event combination. Multiple notifications can specify the same handler; for example, all the buttons on a toolbar might call the same handler for their click event. You can use the source object passed to the event handler to get details about which button was clicked. (Typically, you cast the object passed to the handler into the appropriate class to be able to invoke methods from the appropriate class.)

The following example shows code that defines buttons for a toolbar and requests notification for their click events, along with the method used to handle them.

   private void initEventHandlers() {
      Button buttonNew = new Button();
      Button buttonSave = new Button();
      Button buttonExit = new Button();
      // All events are routed to the same handler
      buttonNew.addOnClick( new MouseEventHandler( this.toolbarClick) );
      buttonSave.addOnClick( new MouseEventHandler( this.toolbarClick) );
      buttonExit.addOnClick( new MouseEventHandler( this.toolbarClick) );
   }
   // common event handler
   private void toolbarClick( Object source, Event e){
      String buttonName;
      if (source instanceof Button) {
         buttonName = new String((Button)source).getText());
         MessageBox.show("You clicked button " + buttonName);
      }
   }


Localizing Your Application

WFC and the Visual J++ Designer provide a simplified means for developing multilingual applications. A WFC application can be created in several localized language versions where the only difference between versions is a binary resource file. The naming convention for each resource file indicates the languages it supports, and the correct resources are loaded at run time according to the user's locale setting.

There are two parts to understanding localization concepts: design-time implementation and run time support. Certain properties of visual elements (forms and controls) are understood by Visual J++ to be localizable. At design time, Visual J++ is used to serialize these localizable properties to a binary resource file. For example, the text, font, and size of a control can change between language versions. At run time, when the application is loaded, the system loads the resources that correspond to the client thread after determining the user's locale.

To create a localized version of your application, design the visual layout using Visual J++, set the form's localizable property to true, and save the form. Visual J++ automatically creates a binary resource file and serializes all localizable properties to it.

When a form's localizable property is set to true, Visual J++ always saves resources to a single resource file with the name Form.resources, where Form is the name of the main form (for example, Form1.resources). Each version that you create will be saved to this resource file name. After creating each new version, you make a copy of this resource file and rename it to the appropriate language locale name using the standard Windows locale suffix (for example, Form1_jpn.resources for a Japanese version) using the Windows Explorer or Microsoft® MS-DOS® commands.

Important Be sure to save your original layout with a new name before laying out and saving any localized versions. This will be your master .resources file.

As an example scenario, assume you want to create American English, French, and Japanese versions of your application, whose main form is called Zippo.java. Also assume you start with the English version (although this is not necessary). First, lay out the form in English and set the localizable property to true. When you save the form, the file Zippo.resources is created. Now use the Windows Explorer or MS-DOS commands to make a copy of Zippo.resource and rename it as Zippo_enu.resources (enu is the local suffix for American English.)

Next, in the Visual J++ Designer, change your default language to French, and lay out the controls with properties localized in French. When you are finished, save the form again, overwriting the previous version of Zippo.resources. Again, make a copy of Zippo.resources and rename it as Zippo_fra.resources.

To test this version, in the Regional Settings dialog box in Control Panel, select the locale: French (Standard), in this case. (There is no need to restart your computer; the locale will be changed on the local thread.)


Using WFC Application Services

The com.ms.wfc.app package contains many classes that provide WFC application services. Many of these operations belong to the Application object itself. These operations have mostly to do with creating threads, starting the application, handling application events, and so on. Because understanding Java threads is important, it is discussed in its own section, Using Java Threads with WFC.

Other operations that qualify as application services pertain to those provided by the Win32 operating system. These include accessing the Windows registry, accessing Clipboard data, and retrieving system information, among other operations.

Starting and Quitting an Application

The Application.run method starts a WFC application. This is typically placed in the main method of the Form-based class that constitutes the main application form. Application.run has overloaded methods that take either no parameters or one parameter specifying the form class that represents the main window of the application. For example, the following is a typical form of this call:

   public static void main(String args[])
   {
      Application.run(new MyMainWindow());
   }

If a form is passed to the run method, the form's visible property is automatically set to true, and an onClosed event handler is added to the form. The onClosed event handler calls the Application.exitThread method when the form is closed. If no form is passed, the application runs until Application.exit is called, shutting down all threads and windows on the application, or until exitThread is called, shutting down just the application's current thread.

Handling Application Events

You use the Application object to assign event handlers for five different events that occur in the context of the application: applicationExit, idle, settingChange, systemShutdown, and threadException. The following addOn methods can be called to define event handlers for these events.

Application Method | Description
addOnApplicationExit | Specifies a handler that is called when the application quits. You can clean up application resources here that will not be released by garbage collection. (To force the application not to quit, specify a handler for the form’s closing event.)
addOnIdle | Specifies a handler that is called when the application’s message queue is idle, for example, to perform background operations or application cleanup.
addOnSettingChange | Specifies a handler that is called when the user changes window settings.
addOnSystemShutdown | Specifies a handler that is called immediately before a system shutdown initiated by a user. This provides an opportunity to save data.
addOnThreadException | Specifies a handler that is called when an untrapped Java exception has been thrown, allowing the application to gracefully handle the exception. This event handler takes a com.ms.wfc.app.ThreadExceptionEvent object, which has one field that represents the exception thrown.

All these "addOn" methods have reciprocal "removeOn" methods to remove the event handler.

Accessing System Information

The Win32 system contains a large amount of information that is accessible to a WFC application or component. Most of this access is through classes in the com.ms.wfc.app package. Much of this information is stored in the Windows registry and accessed through the RegistryKey and Registry classes. Other system information, such as Windows display element sizes, operating system settings, network availability, and the hardware capabilities, are accessed using static methods in the com.ms.wfc.app.SystemInformation class. System time is available using the com.ms.wfc.app.Time class.

This section provides an overview of how a WFC application can access this system information.

Windows Registry Information

The RegistryKey class in the com.ms.wfc.app package contains methods to access the Windows system registry. Use the methods in this class to create and delete subkeys, to get the count and names of subkeys for the current key, and to retrieve, set, and delete values assigned to subkeys.

The com.ms.wfc.app.Registry class contains fields holding RegistryKey objects that represent the root keys of the registry (those starting with HKEY_). (Root RegistryKey objects can also be instantiated using the getBaseKey method.) Methods can be called on any RegistryKey object to enumerate and manipulate keys and key values in the subkey tree below the root object. For example, the following code obtains an array of subKey names under the HKEY_CURRENT_USER key and the number of names in that array:

   int subKeyCount;
   String[] subKeyNames;
   subKeyNames = Registry.CURRENT_USER.getSubKeyNames();
   subKeyCount = Registry.CURRENT_USER.getSubKeyCount();

Similarly, any subkey can be retrieved or set given its path, and subkey value names and data can be retrieved or set given the value name. The following example shows retrieving the most recently used file names in Visual Studio and displaying them in an edit box:

   String path; // Holds the path name.
   String[] valueNames; // Holds array of MRU file names in the key.
   int valueCount; // The number of MRU file names in valueNames.
   path = new    String("Software\\Microsoft\\VisualStudio\\6.0\\FileMRUList");
   RegistryKey subKey = registry.CURRENT_USER.getSubKey( path );
   // Get the file names and the number of file names.
   valueNames = subKey.getValueNames();
   valueCount = subKey.getValueCount();
   if (valueCount > 0)
      for (int i = 0; i < valueCount; ++i){
         // Get the value, which is the actual file name.
         String value = new     String((String)subKey.getValue(valueNames[i]));
         // Concatenate the name ("1", "2", etc.) with the file name value.
         String valString = new String(valueNames[i] + "   " + value);
         // Add this to the edit box.
         edit1.setText(edit1.getText()+ valString +"\r\n");
      }

You can also create new keys, using the createSubKey method and set values in that key using the setValue method.

Locale Information

Locale information provides details about the language and regional settings on the user's computer. There are many characteristics about a language or region that are stored. These include the character set, international telephone codes, how monetary information is displayed, which calendar is used, the measurement system, and so on.

This information is typically set using the Regional Settings dialog box in Control Panel, but it is also available programmatically. In WFC this access is provided through the methods in the com.ms.wfc.app.Locale class and through the many subclasses of Locale that contain field constants that pertain to Locale methods. For details about setting and retrieving this information, see the methods in the Locale class.

Time Information

Another category of system information is time. The com.ms.wfc.app.Time class provides a Time object that has many capabilities, including capturing system time: the default constructor creates a Time object with the system date and time. Beyond retrieving system time information, the Time object is useful for doing many other things, such as comparing Date and Time objects, converting the time to various formats, and storing a Time object for later retrieval.

Time objects, once created, cannot be altered. However, the Time class provides many methods for creating new objects with offset time (such as addSeconds, addMinutes, addHours, addDays, and addYears). Also, there are many methods for retrieving just one of the properties of a Time object, such as the second, minute, hour, day, and so forth.

The Time object in WFC stores time as the number of hundred-nanosecond units since Jan 1, 100ad. The maximum value that can be stored in a WFC Time object is Dec 31, 10,000ad. Converting WFC Time objects to other formats (Strings, Variants, SYSTEMTIME, and so on) can cause loss of accuracy, and not all formats can store this wide of a range.

Do not confuse the Time class with another com.ms.wfc.app class called Timer. Timer is actually a control; however, it is not in the com.ms.wfc.ui package because it does not have a user interface.

Performing Clipboard and Drag-and-Drop Operations

The drag-and-drop feature in WFC is based on the Win32 (OLE) model, which implements a shortcut for copying and pasting data. When you use the Clipboard, you must perform several steps involving selecting the data, choosing Cut or Copy from the context menu, moving to the destination file, window, or application, and choosing Paste from the context menu. (The origin of the data is called the source and the destination is called the target.)

The drag-and-drop feature removes the necessity of using the context menu. Instead, it uses the action of pressing the left mouse button to capture the selected data in the source and releasing the button in the target to drop it. Drag-and-drop operations can transfer any data that can be placed on the Clipboard; consequently, the data formats for drag and drop are the same as those of the Clipboard. Data formats specify, for example, whether the data is text, bitmap, HTML, .wav, and so on. The com.ms.wfc.app.DataFormats class contains fields pertaining to each of the Clipboard formats. These field names (such as CF_TEXT) come straight from the Win32 constant names.

The data for Clipboard and drag-and-drop operations is stored in a com.ms.wfc.app class called DataObject, which implements the IDataObject interface. IDataObject defines methods for setting and retrieving the data, getting a list of data formats in the data object, and querying for the existence a specific data format.

To programmatically place data on and retrieve it from the Clipboard, use the static methods in com.ms.wfc.app.Clipboard. Clipboard.setDataObject takes an IDataObject and places it on the Windows Clipboard; Clipboard.getDataObject returns an IDataObject from the Clipboard. The target must make sure that the data format on the Clipboard is one that it can use. To do this, it should query the data object with the IDataObject.getDataPresent method, passing it a data format that it can accept; getDataPresent returns true if this type of data is present.

Implementing a Drop Source

For any WFC control component (based on com.ms.wfc.ui.Control), the Control.doDragDrop method is called to start the operation. This is typically done in response to the user's moving the mouse with the left button pressed. Therefore, the code is placed in a mouseMove event handler where the MouseEvent object is checked to see if the left button is down, indicating the start of a drag operation. For example, the following is an event handler for a list box control containing file names:

   private void listFiles_mouseMove(Object source, MouseEvent e)
   {
      //if the left button is down, do the drag/drop
      if(this.getMouseButtons()==MouseButton.LEFT)
      {
         String data = (String)listFiles.getSelectedItem();
         listFiles.doDragDrop( data, DragDropEffect.ALL);
      }
   }

The doDragDrop method takes the data to be transferred and a com.ms.wfc.ui.DragDropEffect object. The DragDropEffect class contains the following constants that can be combined using the bitwise OR for the intended mode of the drag-and-drop operation.

DragDropEffect Method | Description
COPY | Specifies that data will not be removed from the source after the transfer.
MOVE | Specifies that data will be removed from the source after the transfer.
SCROLL | Specifies that data will be scrolled in the target after the transfer.
ALL | Specifies that data will be removed from the source after the transfer and scrolled into the target (essentially COPY | MOVE | SCROLL).
NONE | Specifies that no operation is performed.

The target that receives the data in the drag-drop operation receives the dragDrop event, which contains this DragDropEffect object, so it can easily determine the intent of the operation.

Implementing a Drop Target

The drop part of the drag-and-drop operation is handled as an event. The Control class provides the event handler infrastructure for these drag-and-drop events: dragDrop, dragEnter, dragLeave, and dragOver. You can use the following methods to specify handlers for these events.

Control Method | Description
addOnDragDrop | Specifies a handler for data that is dropped into your control or window (when the left mouse button is released).
addOnDragEnter | Specifies a handler for drop data as the cursor first enters the window.
addOnDragLeave | Specifies a handler for drop data as the cursor leaves the window.
addOnDragOver | Specifies a handler for drop data as the cursor is dragged across the window.

All these "addOn" methods have reciprocal "removeOn" methods to remove the event handler. As with all event handler addOn and removeOn methods in WFC, these methods take a delegate (in this case, DragEventHandler) that is created with the name of your handler method. For example, the following line adds the txtFile_dragDrop method as a dragDrop event handler:

txtFile.addOnDragDrop(new DragEventHandler(this.txtFile_dragDrop));

Of all the drag-and-drop events, the dragDrop event is the most commonly handled. Regardless of which of these events is handled, the code in the handler must do at least three things. It must first determine if it can accept the data format, and if so, it must then copy the data and optionally display it (or provide some user interface feedback that the data was dropped).

All data and information is passed in the DragEvent event. This contains, among other fields, a data field and an effect field. The DragEvent.data field contains an object that supports IDataObject that has methods to retrieve the data and the data formats and to query for the existence of a specific format.

Therefore, the handler must first call DragEvent.data.getDataPresent method with a format it will accept and then determine whether it holds that data type. If so, it can then call the DragEvent.data.getData method (passing in that data type) and retrieve the data. How the data is displayed is up to that control. The following example illustrates an edit control dragDrop event handler that displays text data dropped to it.

    private void txtFile_dragDrop(Object source, DragEvent e)
   {
      // If text is in the object, write it into the edit control.
      if (e.data.getDataPresent(DataFormats.CF_TEXT))
      {
         String filename=(String)e.data.getData(DataFormats.CF_TEXT);
         txtFile.setText(filename);
      }
   }


Using Java Threads with WFC

Java is a free-threaded environment. This means that any object can call any other object, at any time, from any thread. Special care must be taken when writing objects so that their methods are atomic and thread-safe.

There are several classes that benefit from being free-threaded, and WFC has provided the locking code to make these objects thread-safe. These classes are as follows:

  • com.ms.wfc.core.Component

  • com.ms.wfc.core.Container

  • com.ms.wfc.ComponentManager

  • com.wfc.ui.Brush

  • com.wfc.ui.Font

  • com.wfc.ui.Pen

On the other hand, any object that derives from com.ms.wfc.ui.Control is apartment-threaded because of the Win32 window that is tied to each control. Additionally, most other objects in the com.ms.wfc.ui package are not synchronized, so they should also be considered to be apartment threaded. Likewise, the com.ms.wfc.io, com.ms.wfc.html, and com.ms.wfc.util packages are not thread-safe.

Mixing Java and Win32 Threading Models

WFC accesses native Win32 constructs (such as windows) from Java objects. The Win32 window manager is apartment-threaded, and Windows automatically marshals calls from one thread to another as needed. When a free-threaded object calls into an apartment-threaded object, the call must marshal to the object's apartment. This means that the free thread is blocked for a period of time while the apartment thread handles the request. Any other calls from free-threaded objects to the apartment call will block until the apartment call is free. Consequently, this can lead to deadlock situations.

So how do Java objects, which are inherently free thread work with WFC controls? Rather than hide when thread transitions occur, WFC makes it the programmer's responsibility to request the transition. The programmer can then design an algorithm in such a way as to prevent deadlocks. This can be done by invoking a delegate on the control's thread, which in turn calls the method specified in the delegate.

To execute a given delegate on the thread that created the control's window handle and contains the message loop, use the Control.invoke or Control.invokeAsync methods from the desired control. It is important to use the control's own thread in case the control needs to re-create its window handle for any reason. The invoke method causes the thread to call the specified callback method and wait for a return. The invokeAsync method causes the thread to call the callback method without waiting for reply. All exceptions on the invoked thread are passed on to the owning control in both cases.

You can also use the Control.createGraphics object to perform background painting and animation techniques on a ui.Graphics object. Whereas the Graphics object is apartment-threaded, the createGraphics call is entirely free-threaded. This allows one thread to create a graphics object for a control in another thread.

Creating and Exiting a Thread

Free-threaded threads can be created using the standard Java method of implementing the java.lang.Runnable interface.

This sample shows a class that implements Runnable and takes two controls (a trackbar and a label) as parameters to its constructor. From the thread's run method, it transitions to the trackbar's thread by calling the trackbar's invokeAsync method. InvokeAsync passes a delegate called tDelegate (an instance of com.ms.app.MethodInvoker) that specifies a callback method called tCallBack. Inside that method, the control's thread can safely manipulate the control's properties, in this case, changing the trackbar's tick style. This causes the trackbar's window handle to be re-created. If the thread was not transitioned as demonstrated here, the trackbar would be re-created on the new thread rather than on the thread containing the message loop; in this case, the control trackbar wouldn't receive any new messages and would fail to respond.

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;

/**
 * Runnable is the interface you need to implement to make a new
 * java thread
 */
public class RunnableClass implements Runnable
{
   final int SLEEP = 500;
   Label l;
   TrackBar tb;

   /**
    * This is the thread for our class.
    */
   Thread thread;

   /**
    * Makes a special delegate so WFC can call it from the control's
    * thread.
    */
   MethodInvoker tDelegate = new MethodInvoker(tCallback);

   /**
    * Make a new Java thread; tell it to begin running via the
    * start() method.
    */
   public RunnableClass (TrackBar tb, Label l)
   {
      this.l  = l;
      this.tb = tb;
      thread  = new Thread(this, "RunnableClass thread");
      thread.start();
   }

   public void run()
   {
      while (true)
      {
         /**
          * Call the specified method from the label's thread.
          */
         tb.invokeAsync (tCallback);
         try
         {
            Thread.sleep (SLEEP);
         }
         catch (InterruptedException e)
         {
         }
      }
   }

   int nCount = 0;
   int nTickStyles[] = {TickStyle.BOTH,
                        TickStyle.BOTTOMRIGHT,
                        TickStyle.NONE,
                        TickStyle.TOPLEFT};
   /**
    * This code is executed on the trackbar's thread.
    */
   private void tCallback()
   {
      int nIndex = nCount % (nTickStyles.length);
      l.setText ("hello from tCallBack: " + nCount);
      tb.setTickStyle (nTickStyles [nIndex]);
      nCount++;

      int nValue = tb.getValue();
      if (nValue >= tb.getMaximum())
         tb.setValue(0);
      else
         tb.setValue (nValue + 1);
   }

   public void stopThread()
   {
      thread.stop();
   }
}

Exiting a thread in this case is just a matter of running the thread's stop method. In this example, the Form class that creates the RunnableClass object calls that object's stopThread method when it is disposed. The following code fragment demonstrates this.

...
import RunnableClass;

public class SimpleRunnable extends Form
{
   /**
    * This is the class that implements the Runnable interface.
    */
   RunnableClass runnableClass;

   public SimpleRunnable()
   {
      // Required for Visual J++ Forms Designer support.
      initForm();
      runnableClass = new RunnableClass (tb, l);
   }

   public void dispose()
   {
      runnableClass.stopThread();
      super.dispose();
      components.dispose();
   }
   Container components = new Container();
   Edit eDescription = new Edit();
   TrackBar tb = new TrackBar();
   Label l = new Label();

   private void initForm()
   {
     // Code to initialize the controls omitted ...
   }

   public static void main(String args[])
   {
      Application.run(new SimpleRunnable());
   }
}

Alternately, to create a new application thread without having to implement the Java Runnable interface or extend java.lang.Thread, you can use the Application.createThread method. The createThread method takes a delegate as a parameter (MethodInvoker is often used, but any delegate can be used). In this case, all logic can be contained in one class, typically the Form-based class of the application. The following example code fragment shows how this works.

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;

public class SimpleAppThread extends Form
{
   final int SLEEP = 700;
   Thread thread;

    // Specify the thread context to run a method on.
   MethodInvoker cbDelegate = new MethodInvoker( cbThrdCallback );

   public SimpleAppThread()
   {
      initForm();
      /**
       *  Creates a new thread and runs the methodInvoker method
       *  on the new thread.  The returned thread object is needed
       *  so we can stop the thread when this form is closed (disposed).
       *  Note that thread.start() is called automatically.
       */
       thread = Application.createThread (new MethodInvoker (this.methodInvoker));
   }

   private void methodInvoker()
   {
      while (true)
      {
         // cbThrdCallback is called on the check box's thread.
         cb.invoke (cbDelegate);
         try
         {
            Thread.sleep (SLEEP);
         }
         catch (InterruptedException e)
         {
         }
      }
   }

   int nCount = 0;
   /**
    * Thread callback that sets check box alignment property.
    * This code is to be executed on the check box's thread.
    */
   private void cbThrdCallback()
   {
      cb.setText ("threadCallback loop: " + nCount++);
      if (nCount % 2 == 0)
         cb.setTextAlign (LeftRightAlignment.LEFT);
      else
         cb.setTextAlign (LeftRightAlignment.RIGHT);
   }

   public void dispose()
   {
      thread.stop();
      super.dispose();
      components.dispose();
   }

   private void cbSuspend_click(Object sender, Event e)
   {
      if (cbSuspend.getChecked())
      {
         cbSuspend.setText ("press to resume thread");
         thread.suspend();
      }
      else
      {
         cbSuspend.setText ("press to suspend thread");
         thread.resume();
      }
   }

   Container components = new Container();
   CheckBox cbSuspend = new CheckBox();
   CheckBox cb = new CheckBox();

   private void initForm()
   {
     // Code to initialize the controls here ...
   }

   public static void main(String args[])
   {
      Application.run(new SimpleAppThread());
   }
}

Here the thread is stopped by calling the thread’s stop method from the form's dispose method. The thread is not in a separate class, so its methods can be called directly from the Form-based class.

The Application class also contains the Application.exitThread method, which closes the thread's message loop and shuts down all windows on the thread (note that it does not stop or exit the thread itself). By way of contrast, Application.exit closes message loops on all threads and closes all windows.

Using Thread Storage

The WFC Application class provides support for Thread Local Storage (TLS). Each thread can allocate a slot of memory for storing data that is specific to the thread. Calling Application.allocThreadStorage returns an index to that slot. To set a value in TLS, call the Application.setThreadStorage with the index and a value you want to set. To retrieve that value, call Application.getThreadStorage. Remember to free any allocated thread storage with a call to Application.freeThreadStorage.

Working with Thread Exceptions

The com.ms.wfc.app class provides a ThreadExceptionDialog class, which is automatically displayed whenever an unhandled exception occurs in a thread. You can gain control of exceptions by using the Application.addOnThreadException method to specify your own thread exception handler. The addOnThreadException method takes a ThreadExceptionEventHandler delegate, which is constructed with your event handler method and the ThreadExceptionEvent class.

Typically, a thread exception event handler queries the exception field of the ThreadExceptionEvent object passed to it to determine the next course of action. From this thread exception handler, you can run the ThreadExceptionDialog and retrieve the dialog results in the same way as any other WFC dialog box: use the Form.showDialog method to launch the dialog box, and compare the returned results with the com.ms.wfc.ui.DialogResult class fields.


Introduction to WFC Programming

Windows Foundation Classes for Java (WFC) is a set of class libraries that works seamlessly with the Rapid Application Development (RAD) tool Microsoft® Visual J++®. The combination of this class framework and RAD tool make it easy to build fast and powerful applications and components for the Microsoft platform using the Java language. WFC brings the Java language to the Microsoft® Win32® platform and the Dynamic HTML (DHTML) object model, which is a W3C standard supported by Microsoft® Internet Explorer 4.0. This means you can use Java to solve real problems today and know that you have a smooth migration path into the future.

A goal of WFC is to provide complete and compelling component and programming models for Java. Because Java provides a unique set of advantages in terms of language features, it is the language of choice for developing Microsoft® Windows® and DHTML applications using this new programming model.

WFC enables developers to quickly build rich Win32 applications and Microsoft® ActiveX® Controls in Java by using the Visual J++ Forms Designer to drag and drop controls onto a form, set properties, and generate event handlers. Developers can also easily access data on a server from their applications and deploy their applications as Windows .EXE files or Internet URLs. Moreover, they can build their own WFC components by using the designer or writing code directly in the editor. Finally, developers can access the DHTML object model using Java classes.

The rich application model takes advantage of the J/Direct technology to access the Win32 API. However, it makes it much easier to program by taking care of details like Window message-handling procedures, message pumps, messages, window handles, and so forth. The application model is open, so experienced Win32 programmers can intermix Microsoft® J/Direct™ calls with WFC classes to add any functionality available on the Win32 platform.

If you are looking for a quick “hands-on” introduction to using Visual J++ to develop a WFC application, take a few minutes to walk through Creating a Windows Application with WFC, which steps you through building and running a simple WFC application.

For more information, see:

  • Getting Started with Controls and Templates

  • A Sample Walkthrough


Getting Started with Controls and Templates

One of the strengths of the WFC component model is that the Visual J++ development environment provides most of the components, forms, menus, and dialog boxes that you typically need. These components can either be extended for your own needs or you can author your own controls. Because Visual J++ provides controls typically used in the Win32 environment, you’ll feel at home with the visual components available from the Visual J++ design environment.

The Visual J++ development environment is tightly integrated with the WFC classes, so most of what you need to know to get started using controls, forms, and menus can be found in Using Visual J++.

For more information, see:

  • Starting with a Form

  • Adding Controls

  • Adding Menus

  • Adding Code


Starting with a Form

Most application windows are represented in WFC programming as forms. A form is used wherever you want a separate overlapping, graphical component, such as a main application window or dialog box. Forms act as containers for controls, allowing you to visually compose applications. Forms have their own properties, which can be set in the Properties window. Syntactically, a form is a Java class derived from the com.ms.wfc.ui.Form class. The Form class extends the com.ms.wfc.ui.Control class, as do the controls that you place on a form. Control encapsulates a Win32 window.

When you create a new project, and choose Windows Application from the New Projects dialog box, a form is automatically created for you. You can add other forms by choosing either Add Item or Add Form from the Project menu.

Once a form is added to your project, the form can be viewed either in design mode or in the Text editor. You can use the Forms Designer to size it and set properties by choosing View Designer from the shortcut menu of the form in Project Explorer. Each form belongs to a nonvisual Application object that contains the main thread of the application. Forms and controls represent the visual Windows components. The integration of the visual and nonvisual aspects of the window are handled completely by WFC framework.

When you open the Text editor on a new form, you'll see the template-based class created for you that contains the necessary syntax of the Form class, including a constructor and a main() method with code that instantiates your form. When you work in design mode, adding controls, setting properties, and so forth, the Forms Designer inserts and modifies a section of this class. See Creating a Form to learn about adding forms to your project.


Adding Controls

Controls are added by dragging them from the Toolbox onto the form, sizing them, and setting their properties in the Properties window. You can drag and drop both ActiveX and WFC controls onto your forms in Visual J++. See Adding Controls to a Form to learn about adding controls from the Toolbox to a form.

WFC controls can be grouped into three categories, although there is no distinction between categories in the way you use them:

  • Intrinsic controls. The basic Windows controls such as buttons, check boxes, edit boxes, list boxes, and so on.

  • Common controls. The Win32 common controls found in comctl32.dll. These include controls such as animations, toolbars, tabs, status bars, and Tree view controls.

  • WFC controls. Custom controls written specifically for the WFC framework.

All existing WFC controls are Java classes found in the com.ms.wfc.ui package.

If you want, you can create your own controls using the WFC packages, either by extending existing controls or writing your own from scratch, and adding them to the Toolbox. The WFC component model makes it easy to expose information about your control's properties and events, enabling your control to work seamlessly with the Visual J++ Forms Designer.


Adding Menus

Menus are added in a manner similar to controls and appear in the Toolbox as simply another control. To add a menu, you drag the MainMenu control from the Toolbox onto the form. Once the menu is enabled on the form, the Forms Designer lets you add submenus and menu items by simply typing them in. Each menu item has an event handler that can be hooked up in the coding phase of development. And, as with controls, all implementation code for the menus and menu items is created automatically for you as you visually create the menu. See Creating Menus for Forms for more information on adding menus to a form.


Adding Code

The Forms Designer helps you to create your initial form class and even provides much of the skeleton code for things like event handlers. However, at some point you need to actually write code to make your application do what you want it to.

Event handler methods are called when an event is triggered from a user-interface element on the form, such as a mouse click on a control. The Forms Designer creates skeleton code for these, so it's mostly a matter of filling in the code you want run when the event occurs. The WFC component model employs a new delegate keyword in the Visual J++ compiler that is the basis of all event handling. While delegates are transparent when using the Forms Designer to hook up handlers for events, they can be used directly for more advanced scenarios, such as sourcing your own events. Since they are essentially the same as a function pointer in other languages, they are useful in a number of ways.

In addition to controls and event handlers, there are many other parts of the WFC library that you will find useful.

  • Graphics support. Several classes in the com.ms.wfc.ui package, including the Graphics class, provide support for accessing Windows graphical services.

  • Dynamic HTML support. The com.ms.wfc.html package provides an extensive set of classes that provide access to the DHTML object model implemented by Internet Explorer (versions 4.0 and later).

  • Data binding support. WFC is designed to use the ActiveX Data Objects (ADO) components to support both simple and complex data binding. Using the DataBinder component, you can bind a field from a recordset to the property of any WFC component. WFC also provides other complex-bound components that interact directly with a recordset.

  • Localization support. WFC provides support for localizing your code much easier by letting you store your resource elements, such as strings, fonts, and bitmaps, in a resource file matching a specific international locale. This simplifies the job of translating user interfaces for multiple languages.

  • Direct Win32 API support. The J/Direct technology provided with Visual J++ enables you to call any dynamic-link library (DLL) from your Java code. WFC is built upon a layer of J/Direct calls to Win32 libraries (implemented in the com.ms.wfc.Win32 and com.ms.wfc.OLE32 packages). Consequently, if you are comfortable with standard Windows programming and want to access those libraries directly, WFC provides the underlying elements (such as device contexts and window handles) to give you ultimate control. While this may be desirable for some specialized applications, most programmers will find the basic services of WFC sufficient.


A Sample Walkthrough

This section contains a brief overview of a simple Visual J++ application called MyNotepad, which is based on the Windows Notepad application. MyNotepad is a Text editor with a File menu containing New, Open, Save, Save As, and Exit menu items. It covers the most primitive functions of an editor, allowing the user to open a file, edit it, and save it to the same file or to a different file.

Note that this application is similar to the JPad application generated using the Visual J++ Application Wizard. However, MyNotepad was not constructed using the Application Wizard; it was written specifically to demonstrate a few basic concepts as clearly as possible. After walking through MyNotepad, the code generated by the Application wizard should be much easier to understand, since it uses most of the same principles.

MyNotepad is essentially a single form (MyNotepad.java) with an edit control and a menu. A second form (NewDialog.java) represents the modal dialog box that prompts the user to save when opening a new file or closing the current file. You can find a complete listing of the code for both these files at the end of this topic.

This application was designed and coded using the Visual J++ Forms Designer and Text editor. Much of the code was automatically generated by the designer. This walkthrough points out what the Forms Designer does for you, the code automatically generated by the designer to create this application, and a few basic concepts for programming a WFC application. Specifically, it describes:

  • Using the Forms Designer to create the applications forms

  • How to start and stop an application

  • The anatomy of a Visual J++ Form template

  • How to handle events

  • How to open a modal dialog box and retrieve user results

  • How to use OpenFileDialog and SaveFileDialog classes to work with files

  • How to use the File stream class for file input/output (I/O)

The Sample Walkthrough ends with:

  • Sample Walkthrough Summary

  • Code Listings


Creating the Application using Visual J++

This section walks through the steps required to create the MyNotepad application in Visual J++. It's helpful to look at the steps in the design environment before jumping into the code generated by the designer for those steps.

  1. Create the main form

    To create the main form, use the New Project dialog box that appears when you first open Visual J++ or select New Project from the File menu. Select the Windows Application icon, type in the name of your application form (MyNotepad in this case), and choose Open, and Visual J++ creates a project with that name.

    The project contains a form called Form1.java by default, which will be renamed to MyNotepad.java in a later step.

  2. Add controls and menus to the form

    The Visual J++ Forms Designer makes it easy to layout the form. To open the form in design mode, select Form1.java in the Project Explorer and then choose Designer from the View menu (or choose View Designer from the shortcut menu). With the form displayed, you can add controls from the toolbox. To access the WFC controls in the toolbox, click on the Toolbox tab or choose Toolbox from the View menu, and click the WFC Controls button in the toolbox to display those controls.

    For this sample, an edit control was added to the form from the toolbox.

    Adding a menu is just as easy: drag the MainMenu control from the toolbox onto the form and place it anywhere; then begin typing in the first box and continue adding menu items in the boxes below or to the right.

    Note that you can create an accelerator key on the menu by entering an ampersand (&) before the desired character. This becomes underlined on the menu.

  3. Set properties on the form and controls

    To set properties, use the Properties window. In this case, most properties were left with default values for the sake of simplicity. The following properties on the edit control were changed: the multiline property was set to true, the doc property to was set to Fill, the scrollBars property was set to Vertical, and the font name property was set to Fixedsys to better emulate Notepad. There may be other properties you'll want to set as well on the form and controls.

    You'll probably want to rename some of the components to make more sense programmatically. Renaming is done by selecting the form or control and setting the name property. In the case of MyNotepad, the following name changes were made:

    Default Name | New Name
    Edit1 | EditBox
    MainMenu1 | Menu
    MenuItem1 | FileMenu
    MenuItem2 | FileMenuNew
    MenuItem3 | FileMenuOpen
    MenuItem4 | FileMenuSave
    MenuItem5 | FileMenuSaveAs
    MenuItem6 | FileMenuExit
    MenuItem7 | HelpMenu
    MenuItem8 | HelpMenuAbout

  4. Change the name of Form1.java

    You may want the main form to have a different name than Form1.java. To do this, select Form1.java in the Project Explorer, right-click and choose Rename from the shortcut menu, and type the new name (in this case, MyNotepad.java).

    If you do change the name, remember that you must change all occurrences of Form1 in the source code. To do this, first close the Forms Designer. Then open the source by choosing View Code from the shortcut menu. Choose Find and Replace from the Edit menu, and replace all instances of Form1 with the new name (for example, replace Form1 with MyNotepad).

  5. Create a dialog box

    The NewDialog dialog box is just another form in the project. To create additional forms, choose Add Form from the Project menu, select Form in the Add Item dialog box, type the name of the new form (NewDialog.java, in this case) and click Open.

    In this case, three buttons were added and named YesButton, NoButton, and CancelButton, with appropriate labels (&Yes, &No, and &Cancel). The button control has a dialogResult property, which is useful when the buttons are used on a modal dialog. For example, if the YesButton control's dialogResult property is set to Yes and the user clicks this button, the dialog box closes and returns DialogResults.Yes. In this case, the dialogResult properties were set as follows:

    Control | dialogResult Property
    YesButton | Yes
    NoButton | No
    CancelButton | Cancel

    One note of interest here is that the Form class has an acceptButton property that determines which button is clicked when the user presses ENTER. In this case the acceptButton property was set to the YesButton control. The Form class also has a cancelButton property that determines which button to click when the ESC key is pressed; this was set to the CancelButton control.

    Likewise, the accelerator (&) characters in the button labels were used to map a specific key to each button (for example, because the label for the YesButton button is "&Yes", pressing Y clicks that button).

    Two additional label controls were then added to display the message text of the dialog box. Finally, the PictureBox control was added to the form and that control's image property was set to a bitmap containing an exclamation graphic.

    Because an image was added to the form, Visual J++ automatically created a resource file (called NewDialog.resources) and serialized the image to this file when the form was saved. The resource file provides a mechanism for localizing the form to different languages, although in this case it is mostly used for packaging. You can also set the localizable property of a form to true to cause a resource file to be added to your project, in which case all resources, including strings are added to the resource file.


Starting and Stopping an Application

The com.ms.wfc.app package includes the static Application class, which takes care of all the Win32 window processing, such as registration, instantiation, handling the message loop, and so on. The main application window is created by calling the Application.run method and passing it your Form-derived object that makes up the visual aspect of the window. This call occurs in the main() method in the Form-based template class generated by Visual J++. The following code was generated for the MyNotepad application:

   public static void main(String args[])
   {
      Application.run(new MyNotepad());
   }

By default, the basic application created by a Visual J++ template is closed using the Window’s exit (X) button in the top-right corner of the application. However, you can programmatically quit the application anywhere in the code by calling the Application.exit method. For example, this application is closed when Exit on the File menu is clicked:

    private void FileMenuExit_click(Object sender, Event e)
    {
        // Call the new file handler to invoke NewDialog
        // to ask if user wants to save current data
        this.FileMenuNew_click(sender, e);
        Application.exit();
    }

Having described the code that is invoked when an application is run, it is worth mentioning how a user runs your application. Visual J++ has rich deployment features, one of which is to create a Windows executable (.EXE) file enabling users to run your WFC applications in a similar manner to other Windows application, assuming the WFC classes are installed on the user's computer. WFC classes are included with the latest Microsoft virtual machine which can be redistributed.


Anatomy of a Visual J++ Form Template

A basic WFC form is a public class extending the Form class with a default constructor and an initForm method. When the Form class is instantiated, the class constructor calls the initForm method, which is where the Forms Designer puts all the code used to initialize the form and control properties. Other code specific to your application follows the call to initForm in the constructor. In the MyNotepad application, the title for the application is set here (although it could have just as well been set in the form's Properties window). The constructor for the MyNotepad application is:

    public MyNotepad()
    {
        // Required for Visual J++ Form Designer support
        initForm();
        this.setBounds(100, 100, 300, 300);
        this.setText("Untitled - MyNotepad");
    }

The Visual J++ Forms Designer adds declarations for any added controls in the main body of the class just before the initForm method. For example, here are the declarations for the objects that make up the MyNotepad.java form:

   /**
   * NOTE: The following code is required by the Visual J++ Forms
   * Designer. It can be modified using the Form editor. Do not
   * modify it using the Text editor.
   */

   Container components = new Container();
   MainMenu Menu = new MainMenu();
   MenuItem FileMenu = new MenuItem();
   MenuItem FileMenuNew = new MenuItem();
   MenuItem FileMenuOpen = new MenuItem();
   MenuItem FileMenuSave = new MenuItem();
   MenuItem FileMenuSaveAs = new MenuItem();
   MenuItem FileMenuExit = new MenuItem();
   MenuItem HelpMenu = new MenuItem();
   MenuItem HelpMenuAbout = new MenuItem();
   Edit editbox = new Edit();

   private void initForm()
   {
    . . .

The Visual J++ Forms Designer creates this declaration code as well as the code in the initForm method that sets properties of the form and the controls placed on the form. The infrastructure for handling events is also tightly integrated with the Forms Designer, which can generate event handler mappings in the initForm method.

The first two statements in the initForm method demonstrate how the Forms Designer sets properties on an object (in this case setting the menu item Text property to “&New”) and uses the object’s addOnClick method to establish a click event handler for the object.

    private void initForm()
    {
      FileMenuNew.setText("&New");
      FileMenuNew.addOnClick(new EventHandler(this.FileMenuNew_click));


Handling Events

Most of the code in the MyNotepad application occurs in the event handler methods that are called when menu items are clicked. The Forms Designer makes it easy to create the skeleton event handlers for events generated by controls on the form. For example, to add a handler method for menu click events, you just double-click the menu item on the form. (While double-clicking directly on a button or a menu item produces the click event handlers, you can just as easily create hander skeleton code for other events by using the Events tab in the Properties window).

The Forms Designer then adds a skeleton event handler method, in which you can add code, to your form’s class, and inserts a MenuItem.addOnClick call for the appropriate MenuItem class in initForm.

For example, when you click on a menu item named FileMenuNew, the following line is added to initForm and a method called FileMenuNew_click is added to your class:

      FileMenuNew.addOnClick(new EventHandler(this.FileMenuNew_click));

The MenuItem.addOnClick method takes an EventHandler object. The EventHandler object is created with a reference to the method to call when that menu item is clicked. Essentially, the MenuItem object monitors mouse clicks and calls each event handler added to it, using the delegate, when the event occurs.

All event handler objects are delegates; the difference between them is the event object they pass to the handler. An EventHandler delegate passes an Event object, which contains information about the event. But a KeyEventHandler, for example, passes a KeyEvent object that extends Event for keydown and keyup events. (KeyEvent contains an extra field specifying the UNICODE character and whether it was combined with a CTRL, SHIFT, or ALT key.)

Most WFC programmers need to know little about delegates because event handlers already exist in the WFC packages for the events they care about and the code to add them is generated by the Forms Designer for the most part.

The following is the event handler in the MyNotepad application that handles the selection of this particular menu item:

    private void FileMenuNew_click(Object sender, Event e)
    {
        // If edit control contains text, check if it should be saved
        if (editbox.getText().length() != 0) {
            // Open NewDialog class as a modal dialog
            int result = new NewDialog().showDialog(this);
            // Retrieve result
            // If Yes button was clicked open Save As dialog box
            if (result == DialogResult.YES)
                this.FileMenuSaveAs_click(sender, e);
            // If No button was clicked clear edit control and set title
            else if (result == DialogResult.NO) {
                editbox.setText("");
                this.setText("Untitled - MyNotepad");
            }
        }
    }

Of course, the Forms Designer creates just the skeleton event handler. The code that opens the custom modal dialog box was added manually.


Implementing a Modal Dialog Box

In the MyNotepad application, when a user clicks New on the File menu, the code in this event handler determines if there is text in the edit control. If so, it opens a modal dialog box that displays a message asking the user if they want to save the text. If the user clicks the Yes button, the MyNotepad.FileMenuSaveAs_click method is called, which allows the user to choose a file and save the current text. If the user clicks No, the edit control is cleared, and the title displayed on the main form becomes “Untitled – MyNotepad”.

In the FileMenuNew_click method, the invocation of this dialog box and retrieval of the dialog result is done in one line as follows:

         int result = new NewDialog().showDialog(this);

While the modal dialog box is open, the dialog result value can be set from within the dialog form. The DialogResult class contains integer constants used for this purpose, but any integer can be returned. The button's dialogResult properties were used in this case, which accomplishes the same purpose of setting the DialogResult value.

As an example, clicking the yesButton control sets DialogResult.Yes. This result is then returned from the showDialog method in the owner class when the dialog box is closed. The integer result returned by showDialog is then used to determine what action to take.

The NewDialog.java form was created as a new form using the same Form template used by the main application form. Note the main() method is not required for modal dialog boxes and was removed from this example (leaving it in does not cause errors, but is not considered good practice). Also, extraneous template comments for this method were removed.

Using a Message Box as a Modal Dialog Box

You can also use a message box instead of a custom modal dialog box for simple cases. The click event handler for the About MyNotepad menu item on the Help menu uses a MessageBox object as follows:

    private void HelpMenuAbout_click(Object sender, Event e)
    {
      MessageBox.show("Version: Visual J++ 6.0", "MyNotepad");
    }


Implementing File Dialog Boxes and File I/O

The remaining code of interest in this application has to do with file I/O and using File Open and File Save dialog boxes. It demonstrates how the WFC classes simplify the job of locating, opening, reading from, and writing to files. The following is a brief summary of the WFC classes used for doing this.

In the WFC class hierarchy, both the com.ms.wfc.ui.OpenFileDialog and com.ms.wfc.ui.SaveFileDialog classes extend com.ms.wfc.ui.FileDialog. FileDialog extends CommonDialog, which is a wrapper for the Win32 common dialog API. All common dialogs are set up with properties such as setTitle and setFilter, and are run by calling the showDialog method. These dialog boxes enable users to choose a file name for opening or saving a file.

The com.ms.wfc.io package contains stream-based I/O classes. The File class, which extends DataStream, contains methods for file I/O. In the case of the MyNotepad application, all that is needed is to open a file, read all of it into the edit control (or write the contents of the edit control to the file), and then close the file.

In the MyNotepad application, all I/O and file dialog code is in the event handler methods for the Open, Save, and Save As items on the File menu. We’ll look at just one of these, the Open menu event handler, because it encapsulates the common dialog and File I/O functionality. The code for FileMenuOpen_click is:

    private void FileMenuOpen_click(Object sender, Event e)
    {
        // Create an Open File dialog box
        OpenFileDialog ofd = new OpenFileDialog();
        // Set up filters and options
        ofd.setFilter("Text Docs (*.txt)|*.txt|All Files (*.*)|*.*");
        ofd.setDefaultExt("txt");
        // Run the Open File dialog box
        int OK = ofd.showDialog();
        // Check result of dialog box after it closes
        if (OK == DialogResult.OK) {
            // Retrieve the filename entered
            fileName = ofd.getFileName();
            // Open a File stream on that filename
            currentDoc = File.open(fileName);
            // Retrieve the length of the file
            int ilength = (int)currentDoc.getLength();
            // Read in ANSI characters to edit buffer
            editbox.setText(currentDoc.readStringCharsAnsi(ilength));
            // Close the file handle
            currentDoc.close();
            fileOpen=true;
            // Set the application's caption
            this.setText(File.getName(fileName) + " - MyNotepad");
        }
    }

When a user clicks Open on the File menu, the FileMenuOpen_click event handler method is called. The first three lines of code in this method create an OpenFileDialog object and set the filters and extensions used by the dialog box. While these lines are manually coded here, you can do the same thing using the Forms Designer by adding an OpenFileDialog object to the form and setting its properties (the initialization code is then placed in the initForm method).

Finally, the OpenFileDialog.showDialog method is called to open the dialog box. When the dialog box closes, this method returns an integer equivalent to DialogResult.OK if the user clicks the OK button, and DialogResult.Cancel if the user clicks the Cancel button. If OK was clicked, the file name is retrieved from the OpenFileDialog object and passed to the File.open method, which returns a File stream opened on the file as read-write access to the file. File.open is a utility function that does the same thing as creating a File object with the following constructor:

File(fileName, File.OPEN, FileAccess.READWRITE, FileShare.NONE);


Sample Walkthrough Summary

A brief tutorial such as this cannot hope to cover every concept of a useful application, even one as simple as the MyNotepad application. However, the concepts of event handling, working with dialog boxes, and simple file I/O are common to most applications, and have been the main focus.

In this walkthrough we haven’t touched on the code in the event handlers for the Save and Save As menu items. However, this code uses the same principles discussed elsewhere, so there should be no surprises when you look at the listings.

It's noteworthy that besides java.lang, none of the other standard Java packages were used in this application. Instead, packages such as com.ms.wfc.io and com.ms.wfc.ui were used to access the underlying power of the Windows API. This provides immense performance and usability gains when you know that your target environment will be a Win32 operating system.

Keep in mind the MyNotepad application was written as a demonstration vehicle. To keep it short and to the point, it doesn't provide much error checking or many of the features of Notepad or even JPad. However, with this quick tour completed you should be well on your way to creating your own Microsoft® Windows® applications using Visual J++ and WFC.


Code Listings

The following code listings are provided for the two form-based classes in the MyNotepad application:

  • MyNotepad.java

  • NewDialog.java


MyNotepad.java

/*******************************************************************
MyNotepad.java
This sample is provided as a companion to the Introduction to WFC
Programming topic in the Visual J++ documentation. Read the section
titled MyNotepad Sample Walkthrough in conjunction with this sample.

********************************************************************/

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.io.*;

public class MyNotepad extends Form
{

    private File currentDoc; // the I/O file stream
    private String fileName; // the most recently-used file name
    private boolean fileOpen = false; // set true after file opened

    public MyNotepad()
    {
        // Required for Visual J++ Forms Designer support
        initForm();
        this.setBounds(100, 100, 300, 300);
        this.setText("Untitled - MyNotepad");
    }

    private void HelpMenuAbout_click(Object sender, Event e)
    {
        MessageBox.show("Version: Visual J++ 6.0", "MyNotepad");
    }

    private void FileMenuNew_click(Object sender, Event e)
    {
        // If edit control contains text, check if it should be saved
        if (editbox.getText().length() != 0) {
            // Open NewDialog class as a modal dialog
            int result = new NewDialog().showDialog(this);
            // Retrieve result
            // If Yes button was clicked open Save As dialog box
            if (result == DialogResult.YES)
                this.FileMenuSaveAs_click(sender, e);
            // If No button was clicked clear edit control and set title
            else if (result == DialogResult.NO) {
                editbox.setText("");
                this.setText("Untitled - MyNotepad");
            }
        }
    }

    private void FileMenuOpen_click(Object sender, Event e)
    {
        // Create an Open File dialog box
        OpenFileDialog ofd = new OpenFileDialog();
        // Set up filters and options
        ofd.setFilter("Text Docs (*.txt)|*.txt|All Files (*.*)|*.*");
        ofd.setDefaultExt("txt");
        // Run the Open File dialog box
        int OK = ofd.showDialog();
        // Check result of dialog box after it closes
        if (OK == DialogResult.OK) {
            // Retrieve the filename entered
            fileName = ofd.getFileName();
            // Open a File stream on that filename
            currentDoc = File.open(fileName);
            // Retrieve length of file
            int ilength = (int)currentDoc.getLength();
            // Read in ANSI characters to edit buffer
            editbox.setText(currentDoc.readStringCharsAnsi(ilength));
            // Close the file handle
            currentDoc.close();
            fileOpen=true;
            // Set the application's caption
            this.setText(File.getName(fileName) + " - MyNotepad");
        }
    }

    private void FileMenuSave_click(Object sender, Event e)
    {
        // If there has been a file opened or saved
        if (fileOpen){
            // Open the current file again
            currentDoc = File.open(fileName);
            // Write edit control contents to file
            currentDoc.writeStringCharsAnsi(editbox.getText());
            // Close file handle
            currentDoc.close();
        }
        else
            this.FileMenuSaveAs_click(sender, e);
    }

    private void FileMenuSaveAs_click(Object sender, Event e)
    {
        SaveFileDialog sfd = new SaveFileDialog();
        // Set the options
        sfd.setFileName (fileName);
        sfd.setTitle("Save Text File");
        sfd.setFilter("Text Docs (*.txt)|*.txt|All Files (*.*)|*.*");
        sfd.setDefaultExt("txt");
        // Run the dialog box
        int result = sfd.showDialog();
        if (result == DialogResult.OK ) {
            // Retrieve the filename entered in the dialog box
            fileName = sfd.getFileName();
            // Open a File stream with ability to create a file if needed
            currentDoc = new File(fileName, FileMode.OPEN_OR_CREATE);
            // Write the contents of the edit control to the file
            currentDoc.writeStringCharsAnsi(editbox.getText());
            // Close the file handle
            currentDoc.close();
            fileOpen = true;
            // Set the app's caption using the filename minus its path
            this.setText(File.getName(fileName) + " - MyNotepad");
        }
    }

    private void FileMenuExit_click(Object sender, Event e)
    {
        // Call the new file handler to invoke NewDialog
        // to ask if user wants to save current data
        this.FileMenuNew_click(sender, e);
        Application.exit();
    }

    /**
     * NOTE:  The following code is required by the Visual J++ Forms
     * Designer. It can be modified using the Form editor. Do not
     * modify it using the Text editor.
     */

    Container components = new Container();
    MainMenu Menu = new MainMenu();
    MenuItem FileMenu = new MenuItem();
    MenuItem FileMenuNew = new MenuItem();
    MenuItem FileMenuOpen = new MenuItem();
    MenuItem FileMenuSave = new MenuItem();
    MenuItem FileMenuSaveAs = new MenuItem();
    MenuItem FileMenuExit = new MenuItem();
    MenuItem HelpMenu = new MenuItem();
    MenuItem HelpMenuAbout = new MenuItem();
    Edit editbox = new Edit();

    private void initForm()
    {
      FileMenuNew.setText("&New");
      FileMenuNew.addOnClick(new EventHandler(this.FileMenuNew_click));

      FileMenuOpen.setText("&Open");
      FileMenuOpen.addOnClick(new EventHandler(this.FileMenuOpen_click));

      FileMenuSave.setText("&Save");
      FileMenuSave.addOnClick(new EventHandler(this.FileMenuSave_click));

      FileMenuSaveAs.setText("Save &As");
      FileMenuSaveAs.addOnClick(new
        EventHandler(this.FileMenuSaveAs_click));

      FileMenuExit.setText("E&xit");
      FileMenuExit.addOnClick(new EventHandler(this.FileMenuExit_click));

      FileMenu.setMenuItems(new MenuItem[] {
                            FileMenuNew,
                            FileMenuOpen,
                            FileMenuSave,
                            FileMenuSaveAs,
                            FileMenuExit});
      FileMenu.setText("&File");

      HelpMenuAbout.setText("&About MyNotepad...");
      HelpMenuAbout.addOnClick(new
        EventHandler(this.HelpMenuAbout_click));

      HelpMenu.setMenuItems(new MenuItem[] {
                            HelpMenuAbout});
      HelpMenu.setText("&Help");

      Menu.setMenuItems(new MenuItem[] {
                        FileMenu,
                        HelpMenu});

      this.setText("MyNotepad");
      this.setVisible(false);
      this.setAutoScaleBaseSize(13);
      this.setClientSize(new Point(302, 314));
      this.setMenu(Menu);

      editbox.setDock(ControlDock.FILL);
      editbox.setFont(new Font("Fixedsys", 8.0f,
        FontSize.POINTS, FontWeight.NORMAL, false, false, false,
        CharacterSet.DEFAULT, 0));
      editbox.setSize(new Point(302, 314));
      editbox.setTabIndex(1);
      editbox.setText("");
      editbox.setMultiline(true);
      editbox.setScrollBars(ScrollBars.VERTICAL);

      this.setNewControls(new Control[] {
                          editbox});
    }

    /**
     * The main entry point for the application.
     *
     * @param args Array of parameters passed to the application
     * via the command line.
     */
    public static void main(String args[])
    {
        Application.run(new MyNotepad());
    }
}


NewDialog.java

/*******************************************************************
NewDialog.java
This sample is provided as a companion to the Introduction to WFC
Programming topic in the Visual J++ documentation. Read the section
titled MyNotepad Sample Walkthrough in conjunction with this sample.

This form represents a simple modal dialog box.

********************************************************************/

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;

public class NewDialog extends Form
{

    public NewDialog()
    {
        // Required for Visual J++ Forms Designer support
        initForm();
    }

    /**
     * NOTE: The following code is required by the Visual J++ Forms
     * Designer. It can be modified using the Form editor. Do not
     * modify it using the Text editor.
     */

    Container components = new Container();
    Label label1 = new Label();
    Label label2 = new Label();
    Button yesButton = new Button();
    Button noButton = new Button();
    Button cancelButton = new Button();
    PictureBox pictureBox1 = new PictureBox();

    private void initForm()
    {
        // NOTE:  This form is storing resource information in an
        // external file. Do not modify the string parameter to any
        // resources.getObject() function call. For example, do not
        // modify "foo1_location" in the following line of code
        // even if the name of the Foo object changes:
        // foo1.setLocation((Point)resources.getObject("foo1_location"));

        IResourceManager resources = new
           ResourceManager(this, "NewDialog");
        label1.setLocation(new Point(90, 20));
        label1.setSize(new Point(210, 20));
        label1.setTabIndex(0);
        label1.setTabStop(false);
        label1.setText("The text in the file may have changed.");

        label2.setLocation(new Point(90, 40));
        label2.setSize(new Point(190, 20));
        label2.setTabIndex(1);
        label2.setTabStop(false);
        label2.setText("Do you want to save the changes?");

        yesButton.setLocation(new Point(20, 90));
        yesButton.setSize(new Point(80, 30));
        yesButton.setTabIndex(2);
        yesButton.setText("&Yes");
        yesButton.setDialogResult(DialogResult.YES);

        noButton.setLocation(new Point(110, 90));
        noButton.setSize(new Point(80, 30));
        noButton.setTabIndex(3);
        noButton.setText("&No");
        noButton.setDialogResult(DialogResult.NO);

        cancelButton.setLocation(new Point(200, 90));
        cancelButton.setSize(new Point(80, 30));
        cancelButton.setTabIndex(4);
        cancelButton.setText("&Cancel");
        cancelButton.setDialogResult(DialogResult.CANCEL);

        this.setText("MyNotepad");
        this.setAcceptButton(yesButton);
        this.setAutoScaleBaseSize(13);
        this.setCancelButton(cancelButton);
        this.setClientSize(new Point(297, 136));

        pictureBox1.setLocation(new Point(20, 20));
        pictureBox1.setSize(new Point(50, 50));
        pictureBox1.setTabIndex(5);
        pictureBox1.setTabStop(false);
        pictureBox1.setText("");

        pictureBox1.setImage((Bitmap)resources.getObject
            ("pictureBox1_image"));

        this.setNewControls(new Control[] {
                            pictureBox1,
                            cancelButton,
                            noButton,
                            yesButton,
                            label2,
                            label1});
    }

}


Delegates in Visual J++ 6.0

Microsoft® Visual J++® 6.0 introduces a powerful new language feature: delegates. Delegates enable scenarios that other languages – C++, Pascal, Modula, and others – have addressed with function pointers. Unlike function pointers, delegates are object-oriented, type-safe, and secure.

A delegate declaration defines a class that extends the class com.ms.lang.Delegate. A delegate instance encapsulates a method – a callable entity. For instance methods, a callable entity consists of an instance and a method on the instance. If you have a delegate instance and an appropriate set of arguments, you can invoke the delegate with the arguments.

An interesting and useful property of a delegate is that it does not know or care about the class of the object that it references. Any object will do; all that matters is that the method’s signature matches the delegate’s. This makes delegates perfectly suited for "anonymous" invocation. This is a powerful capability.

Overview of delegates

A brief overview serves as a better introduction to this feature than a detailed language specification. There are three steps in defining and using delegates: declaration, instantiation, and invocation.

Declaring a delegate

Delegates are declared using Visual J++ delegate declaration syntax, which can appear in the same places in code as class declarations. When compiled, a delegate declaration results in a class that extends either com.ms.lang.Delegate or com.ms.lang.MulticastDelegate, depending on the presence or absence of the multicast keyword.

Here is the syntax:

DelegateDeclaration:

DelegateModifiersopt multicastopt delegate ResultType Identifier (FormalParameterListopt) Throwsopt

DelegateModifiers: zero or more of
public protected private static final

As an example, consider the delegate declaration below, which takes two int parameters, returns a long, and raises no checked exceptions.

delegate long IntOp(int a, int b);

Instantiating a delegate

Visual J++ provides two ways to instantiate a delegate. The "late-bound" way is to use an (Object, String) delegate constructor. Use of this constructor requires a string lookup at runtime. To enable more efficient delegate instantiation, Visual J++ provides language syntax for more efficient, "early-bound" delegate instantiation.

DelegateInstantiationExpression:

new DelegateType ( MethodDesignator )

MethodDesignator:
MethodName
Primary . Identifier
super . Identifier

DelegateType:
a class deriving from com.ms.lang.Delegate

For example, the code below uses early-bound delegate instantiation to create an IntOp delegate that wraps the add method:

long add(int a, int b)
{
return a+b;
}

void test()
{
IntOp op = new IntOp(this.add);
. . .
}

Invoking a delegate

In addition to inheriting the Delegate or MulticastDelegate members, a delegate class also includes a member named "invoke" that can be used to call the method encapsulated by the delegate. The signature of this method is:

  ResultType invoke( FormalParameterListopt ) Throwsopt

For example:

long add(int a, int b)
{
return a+b;
}

void test()
{
IntOp op = new IntOp(this.add);
long result = op.invoke(2, 3);
}

Multicast delegates

A "regular" delegate encapsulates a reference to a method; when the delegate is invoked, the underlying method is called. Visual J++ also provides support for "multicast" delegates; the effect of invoking a multicast delegate may be that multiple methods are invoked. The set of methods called by a delegate's invoke method is referred to as the delegate's "invocation list."

Multicast delegates must have a void result type – a single invocation can’t return multiple results, so a result type wouldn’t make much sense for multicast. When a multicast delegate is invoked, the methods in the invocation list are invoked synchronously, in the order in which they appear in the invocation list. If one of the methods raises an exception, then the multicast ceases, and the exception is propagated to the caller of the "invoke" method.

The base delegate classes make multicast support easy by providing invocation list management. There is a "combine" method that produces a new delegate that merges the invocation lists of two or more delegates, and a "remove" method that removes an item from an invocation list.

How are delegates different from function pointers?

Delegates address many of the scenarios that are addressed by function pointers, though in a more modern manner. It would make no sense to add C++-style function pointers to the Java language. Doing so would violate the language’s goals for object-orientation, type safety, and security. Let’s take these goals one-by-one, and see how delegates differ from C++-style function pointers.

  • Delegates are object-oriented. Delegate declarations are compiled into classes, and delegate instances behave just like other instances. In contrast, C++ function pointers have no relationship at all with the object-oriented features of C++.

  • Delegates are type safe. The Visual J++ compiler and the Microsoft virtual machine enforce type safety of delegates. To be encapsulated by a delegate, a method must precisely match the delegate in the following ways: number of arguments and types, return type, and exceptions thrown. In contrast, C++ function pointers may be cast arbitrarily. Incorrect casting can cause system crashes, and it is possible to do things such as treat an integer value as a function pointer, and vice versa. The same level of power (and danger) is not possible in Visual J++.

  • Delegates are secure. The Microsoft virtual machine ensures that delegates interact well with the security system: less trusted code can't create a delegate onto more trusted code and thus gain illicit access to additional capabilities. In contrast, C++-style function pointers do not work within a trust-based security system.

Example: Sorting

It is useful to examine a real-world example. Consider the problem of writing a sorting library that can sort an array of objects using a comparison method provided by the caller. In this example, we’ll use delegates for the comparison method. The sorting package defines a delegate class. The caller implements a comparison method and instantiates a delegate that encapsulates it. When calling the sort routine, the caller passes an array (what to sort) and a comparison delegate (how to sort).

The first step in creating this sorting library is to define a delegate class that defines the comparison method’s shape. We’ll define the comparison method as taking two parameters of type Object, and returning an integer value. The comparison method returns the value 0 if the two objects are equivalent, the value 1 if (a > b) and the value –1 if (a < b). Here is an appropriate delegate, which we have named "Comparer":

delegate int Comparer(Object a, Object b);

When compiled, this declaration produces a class Comparer that extends Delegate. The Comparer class has an invoke method whose signature is:

int invoke(Object a, Object b);

The next step is to define the sorting method. It takes a Comparer instance as a parameter, and uses this parameter to compare items, as shown below.

static void sort(Object[] items, Comparer c)
{
int i, pivot;
. . .

if (c.invoke(items[i], pivot) < 0)
// less than pivot
else
// greater than or equal to pivot
. . .
}

To use the sorting package, a developer calls the sort method, passing a Comparer instance that encapsulates an appropriate comparison method. In the example shown below, the "stringCompare" method is employed.

void test()
{
String[] stringArr = . . .
Sorter.sort(stringArr, new Comparer(this.stringCompare));
}

int stringCompare(Object a, Object b)
{
String x = (String) a;
String y = (String) b;

return x.compareTo(y);
}

A quick recap: there are three steps involved in defining and using a delegate:

  • Declaration
   delegate int Comparer(Object a, Object b);
  • Instantiation
   Comparer c = new Comparer(this.stringCompare);
  • Invocation
int comparisonValue = c.invoke(a, b);

Delegates and events

Though there are a broad set of uses for delegates, most developers will first encounter delegates when using the Windows Foundation Classes, a rich set of components that ease the development of Windows components and applications. WFC components use delegates to expose events. For example, WFC’s Control base class exposes a number of mouse-oriented events: mouseDown, mouseEnter, mouseLeave, mouseMove, mouseUp, and mouseWheel.

The state for each of these events is packaged in a MouseEvent class. This class provides information about the event: which mouse buttons are depressed, whether the shift key is down, etc. The events also share in common a delegate class named MouseEventHandler. This delegate defines the shape of mouse event handlers.

multicast delegate void MouseEventHandler(
Object sender,
MouseEvent e);

In WFC, components enable event handler hookup via "addOnXxx" and "removeOnXxx" methods, where Xxx is the name of the event. Here is the "add" and "remove" pair for the mouseMove event:

void addOnMouseMove(MouseEventHandler value);
void removeOnMouseMove(MouseEventHandler value);

The example below shows a form that has a mouseMove event handler for a button named "myButton":

class MyForm extends Form
{
Button myButton = new Button();

void myButton_mouseMove(Object sender, MouseEvent e)
{
("mouse moved to " + e.x + "," + e.y);
}

void initForm()
{
. . .
myButton.addOnMouseMove(new MouseEventHandler(this.myButton_mouseMove));
. . .
}
}

Delegates vs. interfaces

Delegates and interfaces are similar in that they enable the separation of specification and implementation. Multiple independent authors can produce implementations that are compatible with an interface specification. Similarly, a delegate specifies the signature of a method, and authors can write methods that are compatible with the delegate specification. If Visual J++ has interfaces already, why does it also need delegates?

Interfaces have a number of shortcomings that limit their usefulness:

  • A class can only implement an interface once.

  • Name collisions between interface members sometimes make it impossible for a class to implement multiple interfaces.

  • A class that implements an interface must expose the implemented members publicly.

Because of these shortcomings, it is frequently necessary to use "adapter classes". An adapter class is a surrogate used by a class that wants to implement an interface but can’t do so, usually because of one of the limitations specified above. This shortcoming is particularly painful when using interfaces for eventing because it is quite common for a container to employ multiple instances of the same class. For example, forms commonly have multiple buttons, multiple text boxes and multiple labels. So with an interface-based event model, a typical form requires multiple adapter classes – approximately one per control instance. The result is a sea of adapter classes, whose collective size and cost easily overwhelm the size and cost of the form itself.

Delegates do not suffer the same problems as interfaces. Taking the points above individually:

  • A class can implement as many methods as it pleases, and create any number of delegate instances that wrap these members. This makes it possible to define all event handlers for a form on the form class itself; adapter classes are not needed.

  • There is no such thing as a "name collision" for a delegate. A method is compatible with a delegate if its signature and return type match the delegate’s; the name of the method is immaterial.

  • Delegates can wrap private members, so it is possible to keep event handlers private – a highly desirable capability – without using adapter classes to hide them away.

Besides avoiding a plague of adapter classes, delegates have a number of other advantages over interfaces for event scenarios:

  • Delegates efficiently deal with the common case of unhandled events. The common case for an event is to be unhandled rather than handled. On the other hand, the common case for a component is to have at least one handled event. To maximize efficiency, an event system must avoid size and speed penalties for these unhandled events.

    Unfortunately, interface-based event systems have significant size and speed overhead for these unhandled cases. To handle one event, an event recipient must implement an entire event interface. Similarly, an event source must raise all of the events in an event interface, whether or not there are interesting event handlers on the other side. The former results in an unnecessary per-event size penalty; the latter results in an unnecessary per-event speed penalty.

    Delegate-based event systems sidestep this problem by employing fine granularity: event handlers are connected one-by-one rather than en masse. Unhandled events have zero cost and handled events have a small cost.

  • Delegates make multicast support a breeze. Because delegates intrinsically support multicast, creating and managing multicast events is nearly trivial. The same task in interface-based event systems is difficult, time consuming and error-prone.

  • Delegates make event wiring easy. Delegates enable event wiring and forwarding scenarios that are difficult to support in interface-based systems.


The Truth about Delegates

Sun Microsystems recently released a white paper that derides delegates as "unnecessary", "detrimental to the language", "harmful", "complex", and "not object-oriented." We strongly disagree with Sun’s technical arguments.

Here is a brief outline of the rest of this document:

1.1 Delegates’ dynamic advantages

1.2 Delegates are simpler than inner classes for event scenarios

1.3 Delegates can be both efficient and portable

1.4 Delegates fit cleanly with existing language and VM features

1.5 Delegates result in leaner source code and executable code

1.6 Delegates do not constrain implementation options

1.7 Delegates are better for multicast

1.8 Delegates are implemented with classes, not inner classes

Delegates’ dynamic advantages

Sun’s white paper identifies the need for "some equivalent of method pointers" to support "pluggable" APIs:

It has been clear from the outset that the original language needed some equivalent of method pointers in order to support delegation and "pluggable" APIs. James Gosling noted this in his keynote at the first JavaOne conference.

We agree with this statement, and believe that a delegate-based event model is exactly the solution to this problem. In fact, we believe that JavaBeans’ interface-based event model precludes JavaBeans from being "pluggable" in some important scenarios.

Let’s first examine the mechanism by which JavaBean components are "plugged together" – the mechanism by which events coming from one JavaBean are wired to methods of another JavaBean. To connect two JavaBeans, the source of the events must be provided with an implementation of a listener interface. However, even if a JavaBean implements a method that is "plug compatible" with an event from another JavaBean (one JavaBean has a public method whose signature matches the event of another JavaBean), it is not possible to directly wire the two together. The target JavaBean would have to implement the event listener interface required by the source JavaBean, which is not only highly unlikely, but also restrictive in that it does not allow the target JavaBean to pick meaningful method names for the event handlers. To complete the wiring of two JavaBeans, "glue code" must be provided. Following Sun’s recommendations, this glue code takes the form of an adapter class which implements the event listener interface and forwards events to methods of the receiver. A Java compiler must compile the glue code before the connection between the two JavaBeans can be made.

Most Java development tools support automatic generation and maintenance of glue code (adapter classes), and allow creation of applications that are "pre-wired" – applications wherein the connections between JavaBeans are determined at development-time but remain static at run-time. This solution is acceptable to developers, but does not address a more dynamic type of application where JavaBeans are wired together at run-time. Given that glue code is required to connect two JavaBeans, it follows that a running application cannot dynamically wire JavaBeans together. A running application is very unlikely to have the ability to arbitrarily create and compile Java code—indeed, an application running in the "Sandbox" (such as an applet) is subject to security restrictions that specifically preclude such dynamic generation of code. Furthermore, the glue code requirement severely complicates "code-less" tools, such as Sun’s own Java Studio and Lotus’ BeanMachine, through which end users can visually create applications by wiring together JavaBeans. Such tools are currently forced to generate and compile Java code in order to produce working applications.

Now contrast the interface-based event model with the delegate-based event model provided by WFC. In the delegate-based model, an event from any component can be connected to a method of any other component as long as the signatures of the event and the method are compatible. The act of connecting the two requires no intervening adapter class, and a development tool does not have to generate and maintain additional source code. Furthermore, a running application can dynamically create such connections. For example, a running application can apply reflection and introspection to "live" components to determine their events and methods, and can freely create connections between the components in a type-safe manner. Finally, while the delegate-based event model is not only simpler to manage for traditional development tools, it is also more suitable for "code-less" tools such as Java Studio and BeanMachine, since such tools would never have to deal with code generation.

Some might shrug off the fact that beans are inherently plug-incompatible by pointing out that Java and JavaBeans are for developers rather than end users. We disagree with this reasoning. Dynamic wiring is important for enabling higher-level programming by both developers and end users. In fact, as we have pointed out, many in the industry are focusing on these problems, producing tools such as Sun’s own Java Studio and Lotus’ BeanMachine. Truly dynamic wiring would provide the following benefits:

  • Reduce the working set size of tools. Tools focused entirely on end users would not need a compiler at all.

  • Reduce the complexity and size of solutions. Generated code is great for developers because it provides complete flexibility. The utility of such flexibility is substantially or entirely reduced for end users, who either primarily or entirely use designers rather than code. Higher-level tools can reduce complexity and size by reducing or eliminating generated code, but this is impossible with JavaBeans because all JavaBeans require glue code.

  • Reduce the runtime redistributable required for solutions that are customizable by end users. Again, no compiler is needed.

We find it ironic that Sun appreciates the importance of these problems enough to create a product focused on these problems, but does not recognize the limitations of its own event model.

Delegates are simpler than inner classes for event scenarios

Sun claims that delegates add complexity to the language:

We believe bound method references are harmful because they detract from the simplicity of the Java programming language.

and that they add little value to offset this complexity:

[Delegates] are no more convenient than adapter objects. Although our analysis indicated that some code examples could be expressed more concisely with specialized method reference syntax, we also found that the additional notational overhead for inner classes was never more than a handful of tokens.

The truth is that delegate are simpler than inner classes for event scenarios. WFC’s delegate-based event model results in more concise, more readable, and conceptually simpler source code than the JavaBeans’ interface-based event model.

Sun seems to correlate simplicity of the language with the number of characters that a developer types. By this measure delegates clearly win, but we believe that the main advantage of delegates is that they are conceptually simpler to use for event scenarios than adapter classes. JavaBeans’ interface-based event model effectively dictates the use of adapter classes. We believe that asking developers to implement a separate adapter class in order to handle an event introduces an absurd amount of unnecessary conceptual complexity.

To illustrate this point, we present one example written two ways: the JavaBeans way and the WFC way. The example is a canonical one: implement a dialog with "OK" and "Cancel" buttons. (The code presented below is abbreviated; click here to download the complete examples).

The JavaBeans version, which uses inner classes to connect event handlers, is below. Note the anonymous adapter classes declared within the jbInit method. Each of these classes implements an event handler interface, and delegates to an event handler. This design pattern, in which the event plumbing is separated from the actual event handler, is typical of the code generated by JavaBean development tools, and is identical to the model advocated by Sun in the inner classes specification.

class SimpleFrame extends DecoratedFrame
{
Button buttonOK = new Button();
Button buttonCancel = new Button();

void buttonOK_actionPerformed(ActionEvent e)
{
System.out.println("OK clicked");
}

void buttonCancel_actionPerformed(ActionEvent e)
{
System.out.println("Cancel clicked");
}

void jbInit() throws Exception
{
buttonOK.setLabel("OK");
buttonOK.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
buttonOK_actionPerformed(e);
}
});

buttonCancel.setLabel("Cancel");
buttonCancel.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
buttonCancel_actionPerformed(e);
}
});

this.add(buttonOK, BorderLayout.NORTH);
this.add(buttonCancel, BorderLayout.SOUTH);
}
}

The WFC version, which uses delegates to connect event handlers, is below. Note the instantiation of EventHandler delegates in the initForm method. These delegates encapsulate the actual event handlers. When the invoke method of a delegate is called, the method it encapsulates is called. For event handlers, this occurs when the event source raises the event. The code in the example was generated using Microsoft® Visual J++® 6.0. Other design patterns are possible with the delegate-based model, but we believe that developers and tools vendors will favor the approach used in the example.

class SimpleForm extends Form
{
Button buttonOK = new Button();
Button buttonCancel = new Button();
void buttonOK_click(Object sender, Event e)
{
System.out.println("Clicked OK");
}
void buttonCancel_click(Object sender, Event e)
{
System.out.println("Clicked Cancel");
}
void initForm()
{
buttonOK.setText("OK");
buttonOK.addOnClick(new EventHandler(this.buttonOK_click));
buttonCancel.setText("Cancel");
buttonCancel.addOnClick(new EventHandler(this.buttonCancel_click));
this.setNewControls(new Control[] {buttonCancel, buttonOK});
}
}

We believe that it is self-evident that the WFC code for connecting an event handler to a button’s click event:

buttonOK.addOnClick(new EventHandler(this.buttonOK_click));

is more concise, more readable, and conceptually simpler than the corresponding JavaBeans code:

buttonOK.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
buttonOK_actionPerformed(e);
}
});

The readability of code that uses an interface-based event model is further reduced due to its coarse granularity. Typically, event handlers are sparse: most components have at least one handled event, but most events are unhandled. Because all methods of an interface must be implemented, JavaBeans’ interface-based event model results in adapter code for all events in an interface, even if just one of these events is handled. This unnecessary code hampers readability, and creates unnecessary runtime overhead. WFC’s delegate-based event model does not suffer from these problems. Events are handled one-by-one, so there is no readability or runtime cost for unhandled events.

Delegates can be both efficient and portable

Sun claims that delegates cannot be both efficient and portable:

Looking "under the hood," it is easy to understand our conclusion that any implementation of bound method references will either be inefficient or non-portable. A portable implementation of delegates that generates code capable of running on any compliant implementation of the Java platform would have to rely on the standard Java Core Reflection API.

The truth is that delegates can be both efficient and portable:

  • There is no technical barrier to adding delegate support to virtual machines besides the Microsoft VM.

  • Delegates are efficient. Invoking a delegate is as fast as invoking a virtual method.

We believe that it is inappropriate to consider language and VM innovations as entirely separate matters, as Sun is doing in the section above. Considering language and virtual machine issues separately is appropriate for some features, but not for all of them. It is unfair to characterize delegates as "non-portable" because existing virtual machines do not support them. The question at hand is whether Java language and Java virtual machines should have support for delegates.

Sun’s arguments regarding implementation of delegates using reflection are irrelevant because it is not possible to correctly implement delegates using reflection. Using delegates, a developer can create a delegate that encapsulates a private method on a class. When the delegate’s invoke method is called, the delegate calls the private method. In order to make such a call, the Delegate base class needs to be able to invoke private methods on classes. Reflection could provide this capability, but it does not. Thus, the language and VM aspects of delegates cannot be discussed separately.

Sun’s implicit insistence to discuss the language and VM aspects of delegates is interesting in another way. Using Sun’s twisted logic, it is easy to conclude that any innovation that requires VM changes is non-portable. Such innovations will occur; the Java platform will not remain fixed for eternity. Sun’s circular arguments make it impossible to have a reasonable discussion about which innovations will be adopted as part of the Java platform. Sun would rather obfuscate than address the admittedly difficult issue of how the Java platform will evolve over time. Denying evolution makes planning for it impossible.

Delegates fit cleanly with existing language and VM features

Sun claims that delegates add complexity and do not fit well with existing Java features:

Delegates add complexity. The type system must be extended with an entirely new kind of type, the bound method reference. New language rules are then required for matching expressions of the form `x.f' to method reference constructors, most notably for overloaded or static methods.

The truth is that delegates fit cleanly with existing language and VM features. The steps involved in resolving a delegate instantiation expression are precisely the same as for resolving an expression involving a method call. It is true that Java’s rules for method resolution result in complex compiler and VM logic, but delegates merely leverage this logic. They do not complicate it further. Delegates define no new language rules related to method resolution.

Sun claims that:

Delegates dilute the investment in VM technologies because VMs are required to handle additional and disparate types of references and method linkage efficiently.

Delegates result in loss of object orientation. Because of their special role and supporting syntax, bound method references are "second-class citizens" among other reference types. For example, while delegate types in Visual J++ are technically classes, they cannot extend classes chosen by the programmer, nor implement arbitrary interfaces.

Delegates are limited in expressiveness. Although bound method references appear to the Java VM as reference types, they are no more expressive than function pointers. For example, they cannot implement groups of operations or contain state, and so cannot be used with Java class library iteration interfaces. We wanted the Java language to be class-based, in which classes do all the work, rather than supplying function pointers as an alternate and redundant way to perform the same delegation tasks.

The truth is that delegates fit well with the existing language, are object-oriented, and are appropriately expressive.

  • Delegates fit well with the existing language. As we noted above, the rules that delegates employ for method resolution are precisely the same as for normal method resolution.

  • Delegates are object-oriented. A delegate declaration results in a class that derives from Delegate; a delegate instance is an instance of a class. We have no idea what Sun means by "disparate types of references."

  • Delegates are not "second-class citizens" for reasons of expressiveness. There is nothing "second-class" about the expressiveness of delegates, just as there is nothing second-class about the expressiveness of other primitive types in Java, such as String and Array. Is the Java language poorer because it is not possible for Java developers to create array classes that cannot extend classes chosen by the programmer or implement arbitrary interfaces? Of course not. And the same logic applies equally well to delegates.

Sun confuses the issues by implicitly comparing delegates with inner classes. We believe that it is interesting to compare a delegate-based event model with an interface-based event model that uses inner classes, but that it is not interesting to compare delegates with inner classes. We do not claim that delegates should replace inner classes, or that inner classes should not exist. We do claim that delegates are generally useful, and that WFC’s delegate-based event model is superior to JavaBeans’ interface-based event model.

Delegates result in leaner source code and executable code

Sun shrugs off the advantages of a delegate-based event model as compared to JavaBeans’ interface-based event model.

Delegates are no more convenient than adapter objects. Although our analysis indicated that some code examples could be expressed more concisely with a specialized method reference syntax, we also found that the additional notational overhead for inner classes was never more than a handful of tokens.

The truth is that delegates result in leaner source code and executable code for event scenarios. To make the comparison between WFC’s delegate-based event model and JavaBeans’ interface-based event model concrete rather than theoretical, we created a simple text editor using three different eventing mechanisms:

  • WFC’s delegate-based event model.

  • JavaBeans’ event model, using adapter classes implemented with top-level classes. For this example, we employed the design pattern recommended in Sun’s JavaBeans specification, which was written before inner classes were added to Java. We used the form designer in Borland JBuilder to generate the code.

  • JavaBeans’ event model, using adapter classes implemented with inner classes. For this example, we employed the event handling design pattern recommended in Sun’s Inner Classes specification.

In each case, the text editor consists of a window that contains a text box. The window has a menu bar that contains the same menu items as the default Windows text editor (Notepad). Each menu item is connected to an event handler. In our simple example, each event handler simply displays a message notifying the user that the feature has not yet been implemented. We chose Notepad as an example primarily because of its size – it is both small enough to be easily understood, and big enough to make extrapolation to a larger example meaningful. (Click here to download the complete examples).

The data below shows the tremendous costs of adapter classes, and that these costs are incurred independent of the mechanism used to implement the adapter classes.

Mechanism | Number of classes | Size of executable files (bytes) | Size of source files (bytes)
Adapter classes (top-level) | 22 | 19,000 | 14,742
Adapter classes (inner) | 22 | 20,806 | 10,842
Delegates | 1 | 5,462 | 9,279

The results are striking:

  • Each of the interface-based examples has 22 classes; the delegate-based example has one class. The reason for this astounding difference is that the interface-based event model requires a unique adapter class for each event source

  • The interface-based examples are four times larger on-disk than the delegate-based example. Bandwidth is too important a commodity to spend on unnecessary adapter classes.

  • The interface-based examples result in 17%-59% more source code than the delegate-based example. Besides being more concise, the delegate-based code is also more readable. This point is subjective, so we suggest that readers judge for themselves by examining the code for our examples.

Sun will likely argue that more advanced techniques can be used to lower the number of adapter classes that are needed. We too have been down this path, and recognize that these other techniques have their own drawbacks (such as slower event dispatching), and minimize the problems rather than solve them. Delegates solve the problems by doing away with the need for adapter classes in the first place.

Sun may also argue that some magical VM innovation is just around the corner, and that this innovation will solve the executable size problems. Each adapter class in the examples above is approximately 600 bytes, which is quite large given how many of them there are. We believe that substantial reduction in this overhead is theoretically possible, but we believe that classes will continue to be expensive for quite some time, both due to the nature of the problem and because reducing the size of class files requires changes to all virtual machines. We believe that a delegate-based event model will continue to have size advantages for many years.

Delegates do not constrain implementation options

Sun claims that a delegate-based event model places undue constraints on the structure of an application:

Method references require the programmer to flatten the code of the application into a single class and choose a new identifier for each action method, whereas the use of adapter objects requires the programmer to nest the action method in a subsidiary class and choose a new identifier for the class name.

The truth is that delegates do not constrain implementation options. WFC’s delegate-based event model allows event handlers to be placed anywhere; the interface-based model forces use of an adapter class.

With the delegate-based event model, developers have a great deal of flexibility. Many developers find it convenient to implement event handlers for a component using private methods on the component’s container. For example, the form below shows a form with two buttons and a click handler for each button.

public class MainForm extends Form
{
Button buttonOK = new Button();
Button buttonCancel = new Button();

public MainForm()
{
buttonOK.addOnClick(new EventHandler(this.buttonOK_click));
buttonCancel.addOnClick(new EventHandler(this.buttonCancel_click));
}

private void buttonOK_click(Object sender, Event e)
{
// Your code here
}

private void buttonCancel_click(Object sender, Event e)
{
// Your code here
}
}

The event model does not dictate the use of this design pattern. Any method on any class may be used, so long as the signature of this method matches the signature of the event being raised. Note that event handlers may be private methods, so adapter classes are not required to hide event handlers from the outside world.

Ironically, it is the interface-based model that constrains the structure of code. Event handlers must appear as public methods on a class that implements the appropriate listener interface. Since a single class cannot implement an interface more than once, this model essentially requires that event handlers appear as members on adapter classes.

Delegates are better for multicast

Sun argues that inner classes are superior to delegates for use in multicast event scenarios:

When implementing new event types in the Swing toolkit, a component author may use the EventListenerList class to manage lists of event handlers. Code using EventListenerList is slightly more verbose than the corresponding code relying on the Microsoft® Visual J++® compiler support for multicasting. On the other hand, coding multicast in the standard Java language is more straightforward and less "magical." This approach is also more easily modified to create variations of the basic event notification procedure, such as running boolean-valued event handlers until one returns true, or adding up the results produced by integer-valued event handlers.

Moreover, the cost model for multicasting in a program written in the unadulterated Java language is easier to understand and (if necessary) modify. For example, each additional listener in a Swing component occupies exactly two additional words, and a component with no listeners uses no storage for the absent listeners. Instead of a one-size-fits-all multicast mechanism, Swing designers and extenders are free to use whichever data structure best fits the application.

The truth is that delegates are superior to inner classes for multicast event scenarios. Taking Sun’s points one-by-one:

  • Sun admits that the use of delegates results in more concise code, but minimizes the degree to which the two models differ. WFC’s event model provides automatic multicast; JavaBeans’ event model does not provide this capability.

  • Sun claims that interface-based multicasting is "more straightforward" and "less magical". There is nothing magical about the automatic multi-casting provided by delegates. The behavior provided in the delegate base classes is a well-documented part of these classes. If Sun believes that any behavior that is encapsulated within a component is "magical", this reflects an extremely warped view of component-based development.

  • Sun claims that the interface-based event model results in code that is more easily modifiable to custom multicast rules, ignoring the drawbacks of this model: that the "boilerplate code approach" is error-prone, and puts unnecessary burden on component developers. Delegates provide uniform multicast behavior by default, while also enabling custom multicast behaviors. Developers can multicast in a standard manner with a single line of code, or multicast in a custom manner by manually iterating over the invocation list.

Sun claims that the costs of multicasting are more easily managed for an interface-based event model than for a delegate-based one. This is not true. Developers can use any data structure they wish to manage a list of connected delegates. The Delegate class provides automatic support for this, but there is no requirement that this support be employed. The event models are equivalent in this area. In either case, a multicast-enabled component can use whatever data structure it pleases to manage a set of connected listeners.

Delegates are implemented with classes, not inner classes

Sun’s inaccurate technical analysis has not been restricted to white papers. In a developer.com article, Sun’s James Gosling comments:

"So [Hejlsberg] basically implemented kind of a funky version of method pointers, from eons ago," Gosling said, returning to his key design objection to the WFCs. "Although under the covers, he actually does it on top of inner classes. Now this looks really silly."

The truth is that delegates are implemented using classes, not inner classes. Gosling’s claim that delegates are implemented on top of inner classes is plainly wrong.


Integrating Java with DirectX Media

Microsoft® DirectX® media is a family of high-level APIs and multimedia controls that provide rich support for animation, streaming, and integration of the different media types. Microsoft® DirectAnimation™ is the DirectX media component that enables you to integrate different media types (two-dimensional images, three-dimensional geometries, sounds, movies, text, and vector graphics). The media can be animated over time and integrated with user and synthetic events. Animations can be constructed for deployment on the Web or used directly within applications or standalone content.

DirectAnimation is a COM API and an underlying runtime that different users can access in different ways. HTML authors can add multimedia and animation to their pages by using the DirectAnimation multimedia controls without programming at all. You can also animate HTML pages by using Microsoft® Visual Basic® Scripting Edition (VBScript), Microsoft® JScript™, and Java applets. Finally, Java, Visual Basic, and C++ application programmers can develop Microsoft ActiveX™ Controls or full applications. Thus, DirectAnimation provides multimedia tools that scale from the HTML author to the C++ programmer.

This section shows how DirectX functionality is exposed to Java, concentrating on the use of the DirectAnimation classes.

The following DirectX Media documents are provided.

Microsoft DirectAnimation Java Reference describes each of the DirectAnimation Java methods including syntax, returned values, and how to use the functions.


Java Reference - Classes and Interfaces-Java

Java Reference - Classes and Interfaces DirectAnimation Animated Header --Java Reference - Classes and Interfaces * Microsoft DirectAnimation SDK

  * Next Topic: Java Reference Introduction

Java Reference - Classes and Interfaces

The Microsoft® DirectAnimation™ Java Reference describes each of the DirectAnimation Java methods, including syntax, returned values, and how to use the function. The reference is organized by classes, listed alphabetically.

The Java Reference Introduction describes the organization of the reference.

  • AppTriggeredEvent Class

  • ArrayBvr Class

  • Bbox2Bvr Class

  • Bbox3Bvr Class

  • Behavior Class

  • BooleanBvr Class

  • BvrCallback Interface

  • BvrsToRun Class

  • CameraBvr Class

  • ColorBvr Class

  • Cycler Class

  • DashStyleBvr Class

  • DXMApplet Class

  • DXMCanvas Class

  • DXMEvent Class

  • DXMException Class

  • EndStyleBvr Class

  • ErrorAndWarningReceiver Interface

  • EventCallbackObject Interface

  • FontStyleBvr Class

  • GeometryBvr Class

  • ImageBvr Class

  • JoinStyleBvr Class

  • LineStyleBvr Class

  • MatteBvr Class

  • MicrophoneBvr Class

  • Model Class

  • ModelMakerApplet Class

  • ModifiableBehavior Class

  • MontageBvr Class

  • NumberBvr Class

  • PairObject Class

  • Path2Bvr Class

  • PickableGeometry Class

  • PickableImage Class

  • Point2Bvr Class

  • Point3Bvr Class

  • Preferences Class

  • PropertyDispatcher Class

  • SoundBvr Class

  • Statics Class

  • StringBvr Class

  • Transform2Bvr Class

  • Transform3Bvr Class

  • TupleBvr Class

  • UntilNotifier Interface

  • Vector2Bvr Class

  • Vector3Bvr Class

  • Viewer Interface

  • Java Defaults

  • Java Event Data

  • Java Hierarchy Chart


Java Reference Introduction-Java

Java Reference Introduction DirectAnimation Animated Header --Java Reference Introduction * Microsoft DirectAnimation SDK
    * Topic Contents

  * Previous Topic: Java Reference
  * Next Topic: AppTriggeredEvent

Java Reference Introduction

The Microsoft® DirectAnimation™ Java Reference is organized by classes. For example, there is a section on the ImageBvr class. Each class contains a description of its methods, including syntax, returned values, and how to use the method. At the end of each class, there is a list of methods that, while relevant to that particular class, are included in the Statics library. This library is very large, and it may be easier to familiarize yourself with its contents by looking at specific classes and seeing what Statics functions are pertinent.

This reference is specific to DirectAnimation. For information about methods that are part of a Java package (for example, repaint), consult a Java reference.


AppTriggeredEvent Class-Java Class

AppTriggeredEvent Class DirectAnimation Animated Header --AppTriggeredEvent Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Java Reference Introduction
  * Next Topic: ArrayBvr

AppTriggeredEvent Class

Creates the event. The event can be used in the until and untilNotify methods to create reactive behaviors based on external events.

In the following example, a cube turns from red to blue, based upon some external application event:

//Turn a cube from red to blue when an external event occurs.
//The following code is a fragment from a larger program.
//Get the cube.
GeometryBvr cube = importGeometry(buildURL(geobase, "cube.x");

//Create the external event.
AppTriggeredEvent appEvent = new AppTriggeredEvent();

//Set up the color of the cube -- red until the event, then blue.
ColorBvr col = (ColorBvr)until(red, appEvent, blue);

//Map the color to all points of the cube.
GeometryBvr coloredCube = cube.diffuseColor(col);

//...elsewhere, when the application receives the event...
appEvent.trigger();

//...now coloredCube (assuming it's running) will turn blue.

AppTriggeredEvent Methods

trigger() | Triggers the event.
trigger(eventData) | Triggers the event and passes the given data to the event handler or notifier.

trigger()

AppTriggeredEvent Class

Triggers the event.

Syntax

public void trigger( );

Remarks

The triggered event occurs as soon as possible.

trigger(eventData)

AppTriggeredEvent Class

Triggers the event and passes the given data to the event handler or notifier.

Syntax

public void trigger( Behavior eventData );

Parameters

eventData
Object that contains any data representing the event.

Remarks

The triggered event occurs as soon as possible.


ArrayBvr Class-Java Class

ArrayBvr Class DirectAnimation Animated Header --ArrayBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: AppTriggeredEvent
  * Next Topic: Bbox2bvr

ArrayBvr Class

Selects an object based on a time-varying index. The following example constructs an ArrayBvr out of a Java array and uses the nth method with a time-varying parameter to cycle through the list of colors:

...ColorBvr[] arr = {red, green, blue, yellow, green, cyan,
magenta};
ArrayBvr arrBvr = new ArrayBvr(arr);

//Build an indexer to repeatedly go from 0 to (length - 1)
//at a rate of 1 unit per second.
NumberBvr indexer = mod(localTime, toBvr(arr.length - 1));

//Use indexer to index into the ArrayBvr.
ColorBvr cyclingCol = (ColorBvr)arrBvr.nth(indexer);

...

ArrayBvr Methods

length | Finds the length of an array behavior.
nth | Selects a Behavior object based on the time-varying value of a NumberBvr.
newUninitBvr | Makes it possible to refer to an array behavior before that behavior has been defined.

length

ArrayBvr Class

Finds the length of an array behavior.

Syntax

public NumberBvr length( );

Return Value

Returns a NumberBvr object that is the length of an array behavior.

nth

ArrayBvr Class

Selects a Behavior object based on the time-varying value of a NumberBvr. It uses the greatest integer value less than the number's value to determine the index. An array's index begins at 0 and any attempt to index beyond its length results in a run-time error.

Syntax

public Behavior nth( NumberBvr a );

Parameters

a
NumberBvr object used as an index into the array behavior. This parameter can also be of type double.

Return Value

Returns the Behavior object.

newUninitBvr

ArrayBvr Class

Makes it possible to refer to an array behavior before that behavior has been defined. With this method, a behavior can be created and used in the definition of other behaviors, while its contents are defined at some later point. (This definition is accomplished with the init method, which is available on all behaviors.)

Syntax

public static ArrayBvr newUninitBvr( ArrayBvr arr );

Parameters

arr
Used to establish what type the array holds.

Return Value

Returns the ArrayBvr object.


Bbox2Bvr Class-Java Class

Bbox2Bvr Class DirectAnimation Animated Header --Bbox2Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ArrayBvr
  * Next Topic: Bbox3bvr

Bbox2Bvr Class

Creates an object that represents a two-dimensional bounding box behavior. The behavior specifies the lower-left and upper-right corners of a box that encloses the non-transparent portions of an image behavior. Although a bounding box may not always be the smallest bounding box achievable for the image, it is useful for determining the extent or size of the visible portions of the image.

Applications never create bounding box behaviors explicitly. Instead, create these behaviors by calling the boundingBox method in the ImageBvr class.

For more information about behaviors, see the Behavior class.

Bbox2Bvr Methods

getMax | Creates a two-dimensional point behavior that specifies the upper-right corner of the bounding box.
getMin | Creates a two-dimensional point behavior that specifies the lower-left corner of the bounding box.
newUninitBvr | Makes it possible to refer to a two-dimensional bounding box behavior before that behavior has been defined.

getMax

Bbox2Bvr Class

Creates a two-dimensional point behavior that specifies the upper-right corner of the bounding box.

Syntax

public Point2Bvr getMax( );

Return Value

Returns the Point2Bvr object.

getMin

Bbox2Bvr Class

Creates a two-dimensional point behavior that specifies the lower-left corner of the bounding box.

Syntax

public Point2Bvr getMin( );

Return Value

Returns the Point2Bvr object.

newUninitBvr

Bbox2Bvr Class

Makes it possible to refer to a two-dimensional bounding box behavior before that behavior has been defined. With this method, it is possible to create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Bbox2Bvr newUninitBvr( );

Return Value

Returns the Bbox2Bvr object.


Bbox3Bvr Class-Java Class

Bbox3Bvr Class DirectAnimation Animated Header --Bbox3Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Bbox2bvr
  * Next Topic: Behavior

Bbox3Bvr Class

Creates an object that represents a three-dimensional bounding box behavior. The behavior specifies the back-lower-left and forward-upper-right corners of a cube that encloses the non-transparent portions of a geometry behavior. Although a bounding box might not always be the smallest bounding box achievable for the geometry, it is useful for determining the extent or size of the visible portions of the geometry.

Applications never create bounding box behaviors explicitly. Instead, these behaviors are created by calling the boundingBox method in the GeometryBvr class.

For more information about behaviors, see the Behavior class.

Bbox3Bvr Methods

getMax | Creates a three-dimensional point behavior that specifies the forward-upper-right corner of the bounding box.
getMin | Creates a three-dimensional point behavior that specifies the back-lower-left corner of the bounding box.
newUninitBvr | Makes it possible to refer to a three-dimensional bounding box behavior before that behavior has been defined.

getMax

Bbox3Bvr Class

Creates a three-dimensional point behavior that specifies the forward-upper-right corner of the bounding box.

Syntax

public Point3Bvr getMax( );

Return Value

Returns the Point3Bvr object.

See Also

getMin

getMin

Bbox3Bvr Class

Creates a three-dimensional point behavior that specifies the back-lower-left corner of the bounding box.

Syntax

public Point3Bvr getMin( );

Return Value

Returns the Point3Bvr object.

See Also

getMax

newUninitBvr

Bbox3Bvr Class

Makes it possible to refer to a three-dimensional bounding box behavior before that behavior has been defined. With this method, it is possible to create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Bbox3Bvr newUninitBvr( );

Return Value

Returns the Bbox3Bvr object.


Behavior Class-Java Class

Behavior Class DirectAnimation Animated Header --Behavior Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Bbox3bvr
  * Next Topic: BooleanBvr

Behavior Class

All behaviors are potentially time-varying and/or reactive values such as colors, numbers and images. They can be used to construct animated objects. All behavior subclasses inherit the methods of this class.

Because all behaviors are potentially time-varying and/or reactive, their values can change as a function of time or as the result of user input. Time-varying behaviors are created by combining time-based default behaviors (such as localTime) with other behaviors. Reactive behaviors are created using the until, untilEx, and untilNotify methods and specifying input events, such as mouse clicks. Complex behaviors can be built by combining existing behaviors. The value of a complex behavior always depends on the values of the behaviors that comprise it.

Behaviors are started either by the createModel method (which means they start at time = 0) or with the untilNotify.notify method, when they are started at the event time. When a behavior runs, its time-varying and reactive qualities are active. This means the user can experience any changes that occur in the behavior as time passes or events occur.

In order to type-check behaviors, all behavior types must be known at construction time. This is why there is no Behavior.newUninitBvr method included in the Behavior class.

For relevant methods from the Statics class, see Statics Methods Relevant to Behavior Objects.

Behavior Methods

bvrHook | Allows a running behavior to be sampled.
debug() | Gives the information about a running behavior.
debug(name) | Gives information about a running behavior.
debug(name, onStart) | Gives information about a running behavior.
debug(name, onStart, std) | Gives information about a running behavior.
duration | Creates an animation fragment by associating a local "stop time" with a behavior.
extract | Extracts the value of the behavior, returning a Java object.
init | Initializes a behavior created by the newUninit method.
repeat | Creates a behavior that repeats itself the number of times specified by repetitions.
repeatForever | Creates a behavior that repeats infinitely.
runOnce | Used when applications must reference a running behavior once it starts running, but don't require the application to explicitly start that behavior.
substituteTime | Creates a new behavior from an existing Behavior and a NumberBvr.

bvrHook

Behavior Class

Allows a running behavior to be sampled.

Syntax

public Behavior bvrHook( BvrCallback callback );

Parameters

callback
BvrCallback interface containing the notify method.

Return Value

Returns the Behavior object that is the value of the running behavior at the time it was sampled.

See Also

BvrCallback

debug()

Behavior Class

Gives the information about a running behavior.

Syntax

public Behavior debug( );

Remarks

This method gives the following information about a running behavior.

  • An integer number (the ID), which distinguishes each instance of the behavior.
  • The global time that the behavior was started.
  • The value of the behavior at the time it was started.

The method assumes the following defaults:

  • The behavior is unnamed.
  • The information is displayed each time an instance of the behavior is started.
  • The information is displayed on the standard output.

This method is unavailable to viewers that don't display the standard output.

Example

The following example shows that, when it was started, the second instance of a behavior was started at a global time of 39.66 seconds, and had a value of 0.5:

2 started at 39.66 result = 0.5

debug(name)

Behavior Class

Gives information about a running behavior.

Syntax

public Behavior debug( String name );

Parameters

name
Name of the behavior.

Return Value

Returns the Behavior object.

Remarks

This method gives the following information about a running behavior.

  • The name of the behavior.
  • An integer number (the ID), which distinguishes each instance of the behavior.
  • The global time that the behavior was started.
  • The value of the behavior at the time it was started.

The method assumes the following defaults:

  • The information is displayed each time an instance of the behavior is started.
  • The information is displayed on the standard output.

This method is unavailable to viewers that don't display the standard output.

Example

The following example shows that, when it was started, the second instance of a behavior called "b" was started at a global time of 39.66 seconds, and had a value of 0.5:

"b" 2 started at 39.66 result = 0.5

The next example displays a solid-colored image whose red and green components are 1, and whose blue component is time-varying. The NumberBvr x initially has a value of "b" (a value that varies over time). When the left button of the mouse is pressed, "b" (and therefore, "x" starts all over again, with a value of 0.5.

The debugging information (shown in the previous example) is displayed each time the left mouse button is pressed.

import com.ms.dxmedia.*;

public class DebugTest extends DXMApplet{
  public DebugTest() {
    // Set the model
    setModel(new DebugTestModel());
  }
}

class DebugTestModel extends Model {
  public void createModel(BvrsToRun extraBvrsToRun) {
      NumberBvr b = (NumberBvr) add(localTime, toBvr(0.5))
      .Debug("b");

      NumberBvr x = NumberBvr.newUninitBvr();
      x.init(until(b, leftButtonDown, x));

      ImageBvr model = solidColorImage(colorRgb(toBvr(1),
      toBvr(1), x));

      setImage(model);
  }
}

debug(name, onStart)

Behavior Class

Gives information about a running behavior.

Syntax

public Behavior debug( String name, boolean onStart );

Parameters

name
Name of the behavior.
onStart
If true, the debug method is called when the behavior is started. If false, the method is called whenever the behavior is sampled. The default is true.

Return Value

Returns the Behavior object.

Remarks

This method gives the following information about a running behavior.

  • The name of the behavior.
  • An integer number (the ID), which distinguishes each instance of the behavior.
  • The global time that the behavior was started or sampled.
  • The local time that the behavior was sampled. (If debug is being called when the behavior is started, then the local time is 0, which is not displayed.)
  • The value of the behavior at the time it was started or sampled.

This method assumes the information is displayed on the standard output.

This method is unavailable to viewers that don't display the standard output.

Example

The following example shows that, when it was started, the second instance of a behavior called "b" was started at a global time of 39.66 seconds, and had a value of 0.5:

"b" 2 started at 39.66 result = 0.5

debug(name, onStart, std)

Behavior Class

Gives information about a running behavior.

Syntax

public Behavior debug(String name, boolean onStart, PrintStream std);

Parameters

name
Name of the behavior.
onStart
If true, the debug method is called when the behavior is started. If false, the method is called whenever the behavior is sampled. The default is true.
std
Output device. The default is the standard output.

Return Value

Returns the Behavior object.

Remarks

This method gives the following information about a running behavior.

  • The name of the behavior.
  • An integer number (the ID), which distinguishes each instance of the behavior.
  • The global time that the behavior was started or sampled.
  • The local time that the behavior was sampled. (If debug is being called when the behavior is started, then the local time is 0, which is not displayed.)
  • The value of the behavior at the time it was started or sampled.

This method is unavailable to viewers that don't display the standard output.

Example

The following example shows that, when it was started, the second instance of a behavior called "b" was started at a global time of 39.66 seconds, and had a value of 0.5:

"b" 2 started at 39.66 result = 0.5

duration

Behavior Class

Creates an animation fragment by associating a local "stop time" with a behavior. The result is a new behavior that is the same as the original behavior for the length of the duration. Once the duration is over, the behavior is a snapshot of the behavior's state when the duration ended.

Syntax

public Behavior duration( NumberBvr length )

Parameters

length
NumberBvr object representing the length of time, in seconds, that the duration lasts. This parameter can also be of type double.

Return Value

Returns the Behavior object.

extract

Behavior Class

Extracts the value of the behavior, returning a Java object. This behavior must have a constant value.

Syntax

public Object extract( );

Return Value

Returns a Java object. For more information about java.lang.Object objects, consult a Java reference.

init

Behavior Class

Initializes a behavior created by the newUninit method.

Syntax

public void init( Behavior a )

Parameters

a
Behavior object to be initialized.

repeat

Behavior Class

Creates a behavior that repeats itself the number of times specified by repetitions.

Syntax

public Behavior repeat( int repetitions );

Parameters

repetitions
Number of times the behavior will repeat.

Return Value

Returns the Behavior object.

repeatForever

Behavior Class

Creates a behavior that repeats infinitely.

Syntax

public Behavior repeatForever( );

Return Value

Returns the Behavior object.

runOnce

Behavior Class

Used when applications must reference a running behavior once it starts running, but don't require the application to explicitly start that behavior.

Syntax

public Behavior runOnce( );

Example

The following code fragment plays 2 movies. The first movie plays from the beginning for 10 seconds. It then fades, over a 2 second interval, into a second movie. (Assume the existence of a fade method.)

ImageBvr movie1Once = (ImageBvr)movie1.runOnce();
ImageBvr movie2Once = (ImageBvr)movie2.runOnce();
  until(movie1Once,
   timer(toBvr(10)),
    until(fade(div(localTime, toBvr(2)), movie1Once, movie2Once),
     timer(toBvr(2)),
      movie2Once);

substituteTime

Behavior Class

Creates a new behavior from an existing Behavior and a NumberBvr. In the new behavior, the number behavior replaces all occurrences of localTime in the original behavior. (This includes behaviors where localTime is implicit, such as imported movies. The method can be used even if localTime isn't explicitly in the code.)

Syntax

public Behavior substituteTime( NumberBvr a );

Parameters

a
NumberBvr object that replaces localTime.

Return Value

Returns the Behavior object.

Example

This method allows behaviors to be, for example, time-scaled to run faster or slower, time-shifted to start at a different time, or frozen at a particular point in time. Here are some examples:

//A point moving 1 unit in x/second, starting at 0
    b0 = point2(localTime, toBvr(0));

    //Create new behavior moving 0.5 units/second,
    //by replacing localTime with localTime/2
    b1 = origBvr.substituteTime(div(localTime, toBvr(2)));

    //Create new behavior moving 1 unit/second,
    //starting at 33, by replacing localTime with localTime + 33
    b2 = origBvr.substituteTime(add(localTime, toBvr(33));

    //Create new behavior moving 2 units/second, starting at 33,
    //by replacing localTime with (localTime *2) + 33
    b3 = origBvr.substituteTime
    (add(mul(localTime, toBvr(2)), toBvr(33));

    //Freeze the original behavior at time 77
    b4 = origBvr.substituteTime(toBvr(77));

    //Tie the new behavior to the x-component of the mouse
    b5 = origBvr.substituteTime(mousePosition.getX());

    //Show that replacements are cumulative.
    // The following sequence:
    c0 = point2(localTime, toBvr(0));
    c1 = c0.substituteTime(add(localTime, toBvr(33)));
    c2 = c1.substituteTime(mul(localTime, toBvr(2)));

    //is equivalent to:
    c0.substituteTime(add(mul(localTime, toBvr(2))), toBvr(33));


BooleanBvr Class-Java Class

BooleanBvr Class DirectAnimation Animated Header --BooleanBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Behavior
  * Next Topic: BvrCallback

BooleanBvr Class

A BooleanBvr is an object that represents a boolean behavior. At any given time, the value of the behavior is either true or false.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to BooleanBvr Objects
  • Statics Fields Relevant to BooleanBvr Objects

BooleanBvr Methods

extract | Extracts the value of the behavior, returning a Java object.
newUninitBvr | Makes it possible to refer to a boolean behavior before that behavior has been defined.

extract

BooleanBvr Class

Extracts the value of the behavior, returning a Java object. This behavior must have a constant value.

Syntax

public Object extract( );

Return Value

Returns a Java object. For more information about java.lang.Object objects, consult a Java reference.

newUninitBvr

BooleanBvr Class

Makes it possible to refer to a boolean behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static BooleanBvr newUninitBvr( );

Return Value

Returns the BooleanBvr object.


BvrCallback Interface-Java Interface

BvrCallback Interface DirectAnimation Animated Header --BvrCallback Interface * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: BooleanBvr
  * Next Topic: BvrsToRun

BvrCallback Interface

BvrCallback Methods

notify Calls the notifier method of the object whenever the behavior is started or sampled.

notify

BvrCallback Interface

Calls the notifier method of the object whenever the behavior is started or sampled.

Syntax

public abstract Behavior notify(
     int id,
     boolean start,
     double startTime,
     double globalTime,
     double localTime,
     Behavior sampledValue,
     Behavior currentRunningBvr
);

Parameters

id
Distinguishes one instance of a behavior from another.
start
Determines if the notifier method is called when the behavior starts or each time it is sampled.
startTime
Time the behavior was first run.
globalTime
Time the behavior was sampled, in global time.
localTime
Time the behavior was sampled, in local time. (If the behavior is sampled when it starts, this value is 0).
sampledValue
Value of the behavior at the time it was sampled. This value is wrapped as a constant behavior.
currentRunningBvr
Behavior that is currently running.

Return Value

Returns the Behavior object.


BvrsToRun Class-Java Class

BvrsToRun Class DirectAnimation Animated Header --BvrsToRun Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: BvrCallback
  * Next Topic: CameraBvr

BvrsToRun Class

A BvrsToRun object is an abstract list used to explicitly run behaviors that are not part of the model. To add these extra behaviors, use the add method. To remove these behaviors, use the remove method.

These behaviors will either be run at the same time as the behaviors included in the model (local time = 0) or when an event occurs.

An example of running a behavior that is not part of the model is a movie with both an English and a Spanish soundtrack. Perhaps, initially, the English soundtrack is played but the Spanish soundtrack can be selected at any time. This means it must have the same start time as the English soundtrack so that it is also in sync with the movie.

BvrsToRun Methods

add | Adds behaviors that are not a part of the model.
remove | Removes behaviors that are not a part of the model.

add

BvrsToRun Class

Adds behaviors that are not a part of the model. When the system starts the model, it retrieves these additional behaviors and runs them with the same start time as the behaviors included in the model.

Syntax

public int add( Behavior extraBvrToRun );

Parameters

extraBvrToRun
Additional behaviors to run.

Return Value

Returns an integer ID that identifies the behavior.

remove

BvrsToRun Class

Removes behaviors that are not a part of the model. To remove a behavior, save the BvrToRun object and use the ID returned by the BvrsToRun.add method.

Syntax

public void remove ( int id );

Parameters

id
Integer identifying the added behavior.


CameraBvr Class-Java Class

CameraBvr Class DirectAnimation Animated Header --CameraBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: BvrsToRun
  * Next Topic: ColorBvr

CameraBvr Class

Creates an object that represents a camera behavior. A camera is used with the render method to project geometry onto an infinite two-dimensional image. Related operations include rendering it into a microphone to extract the sound, or into a pair of microphones to extract stereo sounds.

There are two basic types of cameras: one for parallel projection and one for perspective projection. Both cameras are oriented so that they gaze in the negative z direction, with positive y considered "up." Also, both cameras have a near clipping plane. This plane (a product of Z-buffered rendering) specifies the closest distance at which an object is visible. Objects closer than the near clipping plane cannot be seen.

Cameras are manipulated via standard three-dimensional transform operations. These transforms modify the camera characteristics and also position and orient the cameras. For more information about these operations, see the transform methods described below.

For more information about behaviors, see the Behavior class.

For relevant methods from the Statics class, see Statics Methods Relevant to CameraBvr Objects.

CameraBvr Methods

depth | Creates a new camera behavior that is the result of applying the specified depth to the original behavior.
depthResolution | Creates a new camera behavior by applying the specified depth resolution (res) to the original behavior.
transform | Modifies, moves, or orients a camera.
newUninitBvr | Allows you to refer to a camera behavior before that behavior has been defined.

depth

CameraBvr Class

Creates a new camera behavior that is the result of applying the specified depth to the original behavior.

Syntax

public CameraBvr depth( NumberBvr d );

Parameters

d
NumberBvr object that sets the maximum viewing depth of the CameraBvr. This parameter can also be of type double.

Return Value

Returns the CameraBvr object.

Remarks

By default, cameras have infinite depth, which means that all objects in front of the camera are visible. The new behavior is a CameraBvr whose maximum viewing depth is the NumberBvr depth. If depth < 0, the CameraBvr will be unable to see anything. If you prefer to think in terms of hither and yon, this method sets yon = hither + depth. It is important to note that depth is the limit on the far plane. As with the near plane, the geometry is bounded as tightly as possible for maximum Z-resolution.

Limiting the depth of the camera is useful for preserving depth resolution when using Z-buffered rendering. It is also useful for a camera inside a building, where no viewing region is larger than some value.

depthResolution

CameraBvr Class

Creates a new camera behavior by applying the specified depth resolution (res) to the original behavior.

Syntax

public CameraBvr depthResolution( NumberBvr res );

Parameters

res
NumberBvr object that sets the minimum depth (in camera coordinates) that is guaranteed to be distinct. This parameter can also be of type double.

Return Value

Returns the CameraBvr object.

Remarks

The Z-buffered rendering approach assigns one depth-value (typically one of 65,536 possibilities) to each pixel on the screen. The res is the minimum distance between two objects such that they still appear distinct. This resolution varies as you go from the near clipping plane to the far clipping plane (more precision is lost for far objects) and according to the amount of perspective for the given camera. This method (assuming a minimum distance to be observed for the entire visible space) adjusts the far clipping plane accordingly.

For example, if the projection point is at (0,0,2), the image clip is at (0,0,1), and the depth resolution (res) is 1cm, then the resultant camera will have a viewing depth of slightly over 25.1 meters. It is important to note that depth represents the limit on the far plane. As with the near plane, the geometry will actually be bounded as tightly as possible for maximum Z-resolution.

transform

CameraBvr Class

Modifies, moves, or orients a camera.

Syntax

public CameraBvr transform( Transform3Bvr xf );

Parameters

xf
Transform3Bvr object.

Return Value

Returns the CameraBvr object.

Remarks

Cameras are modified, positioned, and oriented via standard transforms. Scale transforms modify the camera while translations and rotations position and orient the camera. The basic steps for using a camera are defining it, modifying it if necessary, and then placing it in the scene.

It is important to note that all camera scales should be applied before camera rotations, and that only scales, rotations, and translations are supported on cameras. Transformations that do not meet these criteria will yield a camera with undefined behavior.

There are two types of scale transformations called Z-scales and X/Y-scales. Z-scales must be positive, non-zero values. Z-scales affect the position of the near clipping plane (if the clipping plane is set to a non-zero value.) For example, if the near clipping plane is located at 10 (that is, if Z=10), then a scale of [1 1 3] will yield a near clipping plane located at Z=30. More importantly, Z-scales move the projection point for a perspective camera. This means an increasing Z-scale yields a perspective camera that is zooming in, and a decreasing Z-scale yields a perspective camera that is zooming out.

Use X/Y-scales to affect the size of the projected image. One way to think of this is that an X/Y-scale sets the size of the camera relative to the world it's viewing. For example, if you are viewing a microscopic scene, use a small X/Y-scale. On the other hand, if you are viewing a solar system, use a large X/Y-scale. Scaling a camera by some value in X/Y is exactly equivalent to inversely scaling the resulting image by the same amount. For example, scaling the camera by [10 10 1] is the same as scaling the resulting image by [1/10 1/10]. Finally, disparate scales in X and Y can be used to change the aspect ratio of the rendered image.

Once the camera is defined (via construction and scaling), it can be placed in the scene via rotations and translations just as a light source, microphone, or any other geometric object is placed.

This attribute composes values.

newUninitBvr

CameraBvr Class

Allows you to refer to a camera behavior before that behavior has been defined. With this method you can create a camera behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static CameraBvr newUninitBvr( );

Return Value

Returns the CameraBvr object.


ColorBvr Class-Java Class

ColorBvr Class DirectAnimation Animated Header --ColorBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: CameraBvr
  * Next Topic: Cycler

ColorBvr Class

A ColorBvr is an object that represents a color behavior. At any given time, the value of the behavior is a color value that represents a specific color. Each color value consists of a combination of component values that specify either red, green, and blue intensity; or hue, saturation, and lightness.

Because component values are number behaviors (NumberBvr objects), the value of the color behavior may change over time as the number behaviors change.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to ColorBvr Objects
  • Statics Fields Relevant to ColorBvr Objects

ColorBvr Methods

getBlue | Creates a number behavior that represents the value of the blue component of the corresponding color behavior.
getGreen | Creates a number behavior that represents the value of the green component of the corresponding color behavior.
getHue | Creates a number behavior that represents the value of the hue of the corresponding color behavior.
getLightness | Creates a number behavior that represents the value of the lightness of the corresponding color behavior.
getRed | Creates a number behavior that represents the value of the red component of the corresponding color behavior.
getSaturation | Creates a number behavior that represents the value of the saturation of the corresponding color behavior.
newUninitBvr | Enables you to refer to a color behavior before that behavior has been defined.

getBlue

ColorBvr Class

Creates a number behavior that represents the value of the blue component of the corresponding color behavior.

Syntax

public NumberBvr getBlue( );

Return Value

Returns the NumberBvr object.

See Also

getRed, getGreen

getGreen

ColorBvr Class

Creates a number behavior that represents the value of the green component of the corresponding color behavior.

Syntax

public NumberBvr getGreen( );

Return Value

Returns the NumberBvr object.

See Also

getRed, getBlue

getHue

ColorBvr Class

Creates a number behavior that represents the value of the hue of the corresponding color behavior.

Syntax

public NumberBvr getHue( );

Return Value

Returns the NumberBvr object.

See Also

getLightness, getSaturation

getLightness

ColorBvr Class

Creates a number behavior that represents the value of the lightness of the corresponding color behavior.

Syntax

public NumberBvr getLightness( );

Return Value

Returns the NumberBvr object.

See Also

getHue, getSaturation

getRed

ColorBvr Class

Creates a number behavior that represents the value of the red component of the corresponding color behavior.

Syntax

public NumberBvr getRed( );

Return Value

Returns the NumberBvr object.

See Also

getGreen, getBlue

getSaturation

ColorBvr Class

Creates a number behavior that represents the value of the saturation of the corresponding color behavior.

Syntax

public NumberBvr getSaturation( );

Return Value

Returns the NumberBvr object.

See Also

getHue, getLightness

newUninitBvr

ColorBvr Class

Enables you to refer to a color behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static ColorBvr newUninitBvr( );


Cycler Class-Java Class

Cycler Class DirectAnimation Animated Header --Cycler Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ColorBvr
  * Next Topic: DashStyleBvr

Cycler Class

The Cycler object cycles through an array of values when an event occurs. Here is an example that cycles through the colors red, green, blue, yellow, red, green, and so on, each time the left mouse button is pressed:

//Cycle through a series of colors
//Create the array to contain the values
Behavior behavior[4] = {red, green, blue, yellow};
//Construct Cycler object
//Associate values in array with the left mouse button event
Cycler cyl = new Cycler(behaviors, leftButtonDown);
//Set the color equal to the next value in the array
ColorBvr col = (ColorBvr)(cyl.getBvr());

The following example cycles through an array of sounds:

//Create an array of sounds
Behavior[]snds = {snd1, snd2, snd3};
Cycler cycle = new Cycler(snds, leftButtonDown);
SoundBvr currentSnd = (SoundBvr)(cycle.getBvr();

Cycler Methods

Cycler | Creates a cycler object.
getBvr | Retrieves a Behavior object from the array.

Cycler

Cycler Class

Creates a Cycler object.

Syntax

Cycler( Behavior[] arrToCycleThrough, DXMEvent eventToCycleUpon );

Parameters

arrToCycleThrough
Array of values cycled through when an event occurs.
eventToCycleUpon
Event that causes the Cycler object to cycle through the array of values.

getBvr

Cycler Class

Retrieves a Behavior object from the array. The behavior that is returned doesn't itself respond to the provided event. The behavior must be running before the events can have any effect.

Syntax

public Behavior getBvr( );

Return Value

Returns the Behavior object.


DashStyleBvr Class-Java Class

DashStyleBvr Class DirectAnimation Animated Header --DashStyleBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Cycler
  * Next Topic: DXMApplet

DashStyleBvr Class

Creates an object that represents a dashstyle behavior. The behavior defines whether lines are drawn using a continuous stroke or a sequence of dashes.

Use the dashstyle behavior with the dash method of the LineStyleBvr object.

For more information about Behaviors see the Behavior class.

For relevant fields from the Statics class, see Statics Fields Relevant to DashStyleBvr Objects.

DashStyleBvr Methods

newUninitBvr Enables you to refer to a dash-style behavior before that behavior has been defined.

newUninitBvr

DashStyleBvr Class

Enables you to refer to a dash-style behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static DashStyleBvr newUninitBvr( );

Return Value

Returns the DashStyleBvr object.


DXMApplet Class-Java Class

DXMApplet Class DirectAnimation Animated Header --DXMApplet Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DashStyleBvr
  * Next Topic: DXMCanvas

DXMApplet Class

Creates a Java applet for creating and displaying Microsoft® DirectAnimation™ behaviors. By default, DXMApplet provides its own frameloop which means the applications need not provide one explicitly. It is possible, of course, to do so by overriding the default behavior. Here is an example of using the DXMApplet to view a model called MyModel:

import com.ms.dxmedia.*;

//Create an applet called MyApplet
public class MyApplet extends DXMApplet{
    public class MyApplet() {
        // Set the model
        setModel(new MyModel());
    }
}

// Code fragment for MyModel
class MyModel extends Model {
    // The createModel method is where the animation is
    // constructed
    public void createModel(BvrsToRun extraBvrsToRun) {
        //Whatever MyModel does
        ...
    }
}

For relevant methods from the Statics class, see Statics Methods Relevant to DXMApplet Objects.

DXMApplet Methods

DXMApplet | Creates a DXMApplet object.
getCanvas | Gets a DXMCanvas.
getModel | Gets the model that was set by setModel.
setModel | Sets the model that will be used.
start | Starts the canvas rendering DirectAnimation behaviors.
stop | Stops the canvas rendering DirectAnimation behaviors.

DXMApplet

DXMApplet Class

Creates a DXMApplet object.

Syntax

public DXMApplet( );

getCanvas

DXMApplet Class

Gets a DXMCanvas. This method should only be called if you are implementing your own ticking.

Syntax

public DXMCanvas getCanvas( );

Return Value

Returns the DXMCanvas object.

getModel

DXMApplet Class

Gets the model that was set by setModel.

Syntax

public Model getModel( );

setModel

DXMApplet Class

Sets the model that will be used.

Syntax

public void setModel( Model model );

Parameters

model
Model to be set.

Remarks

This method must be called before the model is started. Calling it after the model has started causes an exception.

start

DXMApplet Class

Adds the canvas.

Syntax

public start( );

stop

DXMApplet Class

Stops the canvas rendering DirectAnimation behaviors.

Syntax

public stop( );


DXMCanvas Class-Java Class

DXMCanvas Class DirectAnimation Animated Header --DXMCanvas Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DXMApplet
  * Next Topic: DXMEvent

DXMCanvas Class

Creates a Java canvas for creating and displaying Microsoft® DirectAnimation™ behaviors. By default, DXMCanvas provides its own frameloop which means the applications need not provide one explicitly. It is possible, of course, to do so by overriding the default behavior.

  • DXMCanvas Methods from java.awt.Canvas
  • DXMCanvas Methods from Runnable Interface
  • DXMCanvas Methods from Viewer Interface
  • DXMCanvas Methods

DXMCanvas Methods from java.awt.Canvas

addNotify | Overrides the Java implementation and calls the superclass.
handleEvent | Overrides the Java implementation and calls the superclass.
hide | Overrides the Java implementation and calls the superclass.
paint | Overrides the Java implementation and calls the superclass.
removeNotify | Overrides the Java implementation and calls the superclass.
reshape | Overrides the Java implementation and calls the superclass.
show | Overrides the Java implementation and calls the superclass.
update | Overrides the Java implementation and calls the superclass.

addNotify

DXMCanvas Class

Overrides the Java implementation and calls the superclass. This method should not be overridden.

Syntax

public synchronized void addNotify( );

handleEvent

DXMCanvas Class

Overrides the Java implementation and calls the superclass. This method is called when any event occurs inside the DXMCanvas object.

Syntax

public synchronized boolean handleEvent( Event e );

Parameters

e
The event that occurred.

Return Value

Returns true if the event has been handled and no further action is necessary. Returns false if the event is to be given to the component's parent.

hide

DXMCanvas Class

Overrides the Java implementation and calls the superclass. This method should not be overridden. Hides the DXMCanvas object. The object continues to exist and be in its containing object, but it is not visible and does not have any space allocated for it.

Syntax

public synchronized void hide( );

paint

DXMCanvas Class

Overrides the Java implementation and calls the superclass. This method is called to repaint this canvas.

Syntax

public void paint( Graphics g );

Parameters

g
The graphics context.

removeNotify

DXMCanvas Class

Overrides the Java implementation and calls the superclass. This method should not be overridden. Called when the DXMCanvas object is removed.

Syntax

public synchronized void removeNotify( );

reshape

DXMCanvas Class

Overrides the Java implementation and calls the superclass. Reshapes this component to the specified bounding box in its parent's coordinate space.

Syntax

public synchronized void reshape( int x, int y, int width, int height );

Parameters

x, y
The x- and y-coordinates.
width, height
The width and height of the DXMCanvas object.

show

DXMCanvas Class

Overrides the Java implementation and calls the superclass. This method should not be overridden. Shows the DXMCanvas object. If the object has been made invisible by a call to the hide method, makes the object visible again.

Syntax

public synchronized void show( );

update

DXMCanvas Class

Overrides the Java implementation and calls the superclass. Called in response to a call to repaint.

Syntax

public void update( Graphics g );

Parameters

g
The object to be updated.

DXMCanvas Methods from Runnable Interface

run Implements the Runnable interface to create a background thread for ticking the model.

run

DXMCanvas Class

Implements the Runnable interface to create a background thread for ticking the model. For more information about Runnable, consult a Java reference.

Syntax

public void run( );

DXMCanvas Methods from Viewer Interface

getCurrentTime | Returns the number of seconds since startModel was invoked.
getCurrentTickTime | Returns the time provided for the most recent call to tick or 0 if tick has not yet been called.
getPreferences | Allows the application to dynamically view the viewer preferences.
registerErrorAndWarningReceiver | Allows the application to register an object that will have its methods invoked when errors are encountered.
startModel | Starts the behaviors associated with the model at global time 0.
stopModel | Forces all media to stop immediately.
tick() | Provides a shorthand way of writing tick(getCurrentTime()).
tick(timeToUse) | Explicitly performs a single sampling and display of the model, where timeToUse represents the global time.

getCurrentTime

DXMCanvas Class

Returns the number of seconds since startModel was invoked.

Syntax

public double getCurrentTime( );

Return Value

Returns a double floating-point number.

getCurrentTickTime

DXMCanvas Class

Returns the time provided for the most recent call to tick or 0 if tick has not yet been called.

Syntax

public void getCurrentTickTime( );

Return Value

Returns the double floating-point number representing the current tick time.

getPreferences

DXMCanvas Class

Allows the application to dynamically view the viewer preferences. For more information about preferences, see Preferences.

Syntax

public Preferences getPreferences( );

Return Value

Returns the Preferences object.

registerErrorAndWarningReceiver

DXMCanvas Class

Allows the application to register an object that will have its methods invoked upon asynchronous errors encountered when DirectAnimation media is controlling the frame loop or when recoverable errors occur when sampling and displaying the model.

Syntax

public ErrorAndWarningReceiver registerErrorAndWarningReceiver
( ErrorAndWarningReceiver w );

Parameters

w
ErrorAndWarningReceiver object to be registered.

Return Value

Returns the ErrorAndWarningReceiver object.

startModel

DXMCanvas Class

Starts the behaviors associated with the model at global time 0. Also causes the createModel method on the model itself to be invoked.

Syntax

public void startModel( );

stopModel

DXMCanvas Class

Forces all media to stop immediately. It is not required to call this method, however, without it, some media (such as sounds) will continue playing until the object is released.

Syntax

public void stopModel( );

tick()

DXMCanvas Class

Provides a shorthand way of writing tick(getCurrentTime()). It is the easiest way to explicitly sample and display model and provides a real-time clock for global time.

Syntax

public void tick( );

tick(timeToUse)

DXMCanvas Class

Explicitly performs a single sampling and display of the model, where timeToUse represents the global time.

Syntax

public synchronized void tick ( double timeToUse );

Parameters

timeToUse
Global time for the single sampling.

DXMCanvas Methods

addModel | Creates a background thread to tick the model and calls startModel.
addModel - abstract method | Implement this to override addModel.
DXMCanvas | Creates a DXMCanvas object.
getModel | Gets the model that was set by setModel.
hideModel | In the default implementation, pauses the background thread.
hideModel - abstract method | Implement this to override hideModel.
removeModel | Terminates the thread and then calls stopModel.
removeModel - abstract method | Implement this to override removeModel.
setModel | Sets the model that will be used.
showModel | In the default implementation, ensures that the thread is resumed.
showModel - abstract method | Implement this to override showModel.

addModel

DXMCanvas Class

Creates a background thread to tick the model and calls startModel. To perform manual ticking, override this method.

Syntax

public synchronized void addModel( );

addModel - abstract method

DXMCanvas Class

Implement this to override addModel.

Syntax

public abstract synchronized void addModel( );

DXMCanvas

DXMCanvas Class

Creates a DXMCanvas object.

Syntax

public DXMCanvas( );

getModel

DXMCanvas Class

Gets the model that was set by setModel.

Syntax

public Model getModel( );

hideModel

DXMCanvas Class

In the default implementation, this method pauses the background thread. This method can be overridden.

Syntax

public synchronized void hideModel( );

hideModel - abstract method

DXMCanvas Class

Implement this to override hideModel.

Syntax

public synchronized abstract void hideModel( );

removeModel

DXMCanvas Class

This method terminates the thread and then calls stopModel. This method can be overridden.

Syntax

public synchronized void removeModel( );

removeModel - abstract method

DXMCanvas Class

Implement this to override removeModel.

Syntax

public synchronized abstract void removeModel

setModel

DXMCanvas Class

Sets the model that will be used.

Syntax

public void setModel( Model model );

Parameters

model
The model to be set.

showModel

DXMCanvas Class

In the default implementation, this method ensures that the thread is resumed. This method can be overridden.

Syntax

public synchronized void showModel( );

showModel - abstract method

DXMCanvas Class

Implement this to override showModel.

Syntax

public synchronized abstract void showModel( );


DXMEvent Class-Java Class

DXMEvent Class DirectAnimation Animated Header --DXMEvent Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DXMCanvas
  * Next Topic: DXMException

DXMEvent Class

Creates an object that represents an event. Events identify specific times, states or user actions and are used to specify when given actions should occur. Typically, you use events with the until and untilNotify methods to create behaviors that change as a result of the event. For example, you can change the color of an object when the user presses a mouse button by specifying the mouse button event in a call to the until method of the ColorBvr class. Every event, when it occurs, returns some event data. This data may or may not be meaningful. The data returned, for example, by leftButtonDown has no meaning.

You can combine existing events to create new, more complex events. For example, the andEvent method lets you combine two events so that the resulting event occurs only if both of the original events occur simultaneously.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to DXMEvent Objects
  • Statics Fields Relevant to DXMEvent Objects

DXMEvent Methods

DXMEvent | Constructs a DXMEvent object.
attachData | Takes an arbitrary object and produces a new event.
notifyEvent | Creates a new event.
registerCallback | Causes an external action when an event occurs in DirectAnimation for Java and is used in conjunction with the EventCallbackObject.
snapshotEvent | When called on an instance of a behavior, samples the given behavior and returns it as a constant behavior with the value as data.
newUninitBvr | Enables you to refer to a DXMEvent behavior before that behavior has been defined.

DXMEvent

DXMEvent Class

Constructs a DXMEvent object.

Syntax

public DXMEvent( );

attachData

DXMEvent Class

Takes an arbitrary object and produces a new event. The new event occurs at the same time as the original event, but its data is now the data that was specified in the call to attachData.

Syntax

public DXMEvent attachData( Object object );

Parameters

object
Arbitrary object.

Return Value

Returns a DXMEvent object. The event data is the event data of the new event.

Remarks

This method allows an application to associate arbitrary client data with an event and know that it will be delivered to the notifier when the event occurs. In the following example, the behavior is red until the left mouse button is pressed. It then passes either green or yellow, depending on what is returned by the notifier.

DXMEvent greenLeft = leftButtonDown.attachData(green);
DXMEvent yellowright = rightButtonDown.attachData(yellow);
ColorBvr myBvr = (ColorBvr)untilNotify
(red, orEvent(greenLeft, yellowRight), notifier);

notifyEvent

DXMEvent Class

Creates a new event. This occurs when the original event occurs. It then calls the notifier and uses the result as its event data.

Syntax

public DXMEvent notifyEvent( UntilNotifier notifier );

Parameters

notifier
Returns the new behavior.

Return Value

Returns the DXMEvent object. The event data is the time the event fired and the new behavior.

registerCallback

DXMEvent Class

Causes an external action when an event occurs in DirectAnimation for Java and is used in conjunction with the EventCallbackObject. Whenever that event occurs, the invoke method of the callback is called with the event data produced by the event.

Syntax

public Object registerCallback(
     EventCallbackObject object,
     BvrsToRun extraBvrsToRun,
     boolean bool
);

Parameters

object
Object that implements the EventCallback interface.
extraBvrsToRun
Behaviors that are not part of the model.
bool
If true, the first occurrence of the event will trigger invoke. If false, every occurrence of the event will trigger invoke.

Return Value

Returns an object of type java.lang.Object. This object can be used to unregister the callback.

Remarks

An example of where this method is used is displaying a GUI window when the mouse moves to a particular area.

See Also

unregisterCallback

snapshotEvent

DXMEvent Class

When called on an instance of a behavior, samples the given behavior and returns it as a constant behavior with the value as data. The event time of the new event is when the original event occurs.

Syntax

public DXMEvent snapshotEvent( Behavior a );

Parameters

a
Behavior object which will be sampled when the method is invoked.

Return Value

Returns the DXMEvent object. The event data is the value of the behavior at the time it was sampled, returned as a constant behavior.

Example

In the following example, the value of n is localTime until the left mouse button is pressed. It then becomes whatever the value of localTime was when the event occurred:

n=until(localTime, leftButtonDown.snapshotEvent(localTime));

newUninitBvr

DXMEvent Class

Enables you to refer to a DXMEvent behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static newUninitBvr( );

Return Value

Returns the DXMEvent object.


DXMException Class-Java Class

DXMException Class DirectAnimation Animated Header --DXMException Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DXMEvent
  * Next Topic: EndStyleBvr

DXMException Class

DXMException objects are thrown when any detected error occurs in the Microsoft® DirectAnimation™ system. They contain a string that should describe the source or the cause of the exception.

DXMException Methods

DXMException | Constructs a DXMException object.
getErrorCode | Extracts the Microsoft® Win32® error code.
getErrorString | Extracts the error string from the exception.

DXMException

DXMException Class

Constructs a DXMException object.

Syntax

public DXMException( int error, String string );

Parameters

error
Win32 error code. Consult a Win32 reference for more information.
string
Describes the cause or source of the exception.

getErrorCode

DXMException Class

Extracts the Win32 error code. Consult a Win32 reference for more information.

Syntax

public int getErrorCode( );

Return Value

Returns the integer error code.

getErrorString

DXMException Class

Extracts the error string from the exception.

Syntax

public String getErrorString( );

Return Value

Returns an error string of type java.lang.String.


EndStyleBvr Class-Java Class

EndStyleBvr Class DirectAnimation Animated Header --EndStyleBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DXMException
  * Next Topic: ErrorAndWarningReceiver

EndStyleBvr Class

Creates an object that represents an endstyle behavior. The behavior defines the style (shape) applied to the end of line segments when drawn.

Use the endstyle behavior with the end method of the LineStyleBvr object.

For relevant fields from the Statics class, see Statics Fields Relevant to EndStyleBvr Objects.

EndStyleBvr Methods

newUninitBvr Enables you to refer to an EndStyleBvr behavior before that behavior has been defined.

newUninitBvr

EndStyleBvr Class

Enables you to refer to an EndStyleBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static EndStyleBvr newUninitBvr( );

Return Value

Returns the EndStyleBvr object.


ErrorAndWarningReceiver Interface-Java Interface

ErrorAndWarningReceiver Interface DirectAnimation Animated Header --ErrorAndWarningReceiver Interface * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: EndStyleBvr
  * Next Topic: EventCallbackObject

ErrorAndWarningReceiver Interface

This interface, in conjunction with Viewer.registerErrorAndWarningReceiver, allows the application to register an object that will have its methods called when errors and warnings occur in the DirectAnimation system.

ErrorAndWarningReceiver Methods

handleError | Invoked when an error occurs outside of the invocation of a tick(). In addition, ticking is halted.
handleTickError | Invoked specifically for error messages incurred inside of a tick. In addition, ticking is halted.
handleTickWarning | Invoked specifically for warning messages incurred inside of a tick().
handleWarning | Invoked when a warning occurs outside of the invocation of a tick().

handleError

ErrorAndWarningReceiver Interface

Invoked when an error occurs outside of the invocation of a tick(). In addition, ticking is halted.

Syntax

public abstract void handleError(
     int error,
     String s,
     Viewer v
);

Parameters

error
Microsoft® Win32® error code. Consult a Win32 reference for more information.
s
Error message.
v
Viewer object on which the warning occurred.

handleTickError

ErrorAndWarningReceiver Interface

Invoked specifically for error messages incurred inside of a tick. In addition, ticking is halted.

Syntax

public abstract void handleTickError(
     int error,
     String s,
     Viewer v
);

Parameters

error
Win32 error code. Consult a Win32 reference for more information.
s
Error message.
v
Viewer object on which the error occurred.

handleTickWarning

ErrorAndWarningReceiver Interface

Invoked specifically for warning messages incurred inside of a tick().

Syntax

public abstract void handleTickWarning(
     int error,
     String s,
     Viewer v
);

Parameters

error
Win32 error code. Consult a Win32 reference for more information.
s
Warning message.
v
Viewer object on which the error occurred.

handleWarning

ErrorAndWarningReceiver Interface

Invoked when a warning occurs outside of the invocation of a tick().

Syntax

public abstract void HandleWarning(
     int error,
     String s,
     Viewer v
);

Parameters

error
Win32 error code. Consult a Win32 reference for more information.
s
Warning message.
v
Viewer object on which the error occurred.


EventCallbackObject Interface-Java Interface

EventCallbackObject Interface DirectAnimation Animated Header --EventCallbackObject Interface * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ErrorAndWarningReceiver
  * Next Topic: FontStyleBvr

EventCallbackObject Interface

Used in conjunction with registerCallback to cause an external action when an event occurs in Microsoft® DirectAnimation™. Whenever that event occurs, the invoke method of the callback is called with the event data produced by that event.

EventCallbackObject Methods

invoke Used to cause an external action to occur when an event occurs in DirectAnimation for Java.

invoke

EventCallbackObject Interface

Used to cause an external action to occur when an event occurs in DirectAnimation for Java.

Syntax

public abstract void invoke(
     Object object,
     BvrsToRun extraBvrsToRun
);

Parameters

object
Object to be invoked.
extraBvrsToRun
Behaviors that are not part of the model.


FontStyleBvr Class-Java Class

FontStyleBvr Class DirectAnimation Animated Header --FontStyleBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: EventCallbackObject
  * Next Topic: GeometryBvr

FontStyleBvr Class

The FontStyleBvr class is used to specify how strings will be rendered into images. These methods are similar to the HTML tags specifying the font face (called here a family), size, color and attributes (bold and italic).

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to FontStyleBvr Objects
  • Statics Fields Relevant to FontStyleBvr Objects

FontStyleBvr Methods

bold | Specifies that, when the string is rendered into an image, it will be in bold.
color | Specifies the color of the string when it is rendered into an image.
family | Specifies the font face that will be used when the image is rendered.
italic | Specifies that, when the string is rendered into an image, it will be italicized.
size | Specifies the size of the font, in points.
strikethrough | Specifies that, when the string is rendered into an image, it will be struck through.
textAntialiasing | Determines whether the text will be antialiased.
underline | Specifies that, when a string is rendered into an image, it will be underlined.
weight | Specifies the thickness of the boldness of the font.
newUninitBvr | Enables you to refer to a FontStyleBvr behavior before that behavior has been defined.

bold

FontStyleBvr Class

Specifies that, when the string is rendered into an image, it will be in bold.

Syntax

public FontStyleBvr bold( );

Return Value

Returns the FontStyleBvr object.

color

FontStyleBvr Class

Specifies the color of the string when it is rendered into an image. Because this is a ColorBvr object, the color can vary over time.

Syntax

public FontStyleBvr color( ColorBvr color );

Parameters

color
ColorBvr object specifying the color of the string.

Return Value

Returns the FontStyleBvr object.

family

FontStyleBvr Class

Specifies the font face that will be used when the image is rendered. Because this is a StringBvr object, the face can vary over time.

Syntax

public static FontStyleBvr family( StringBvr face );

Parameters

face
StringBvr object specifying the font face. This behavior can also be of type java.lang.String.

Return Value

Returns the FontStyleBvr object.

italic

FontStyleBvr Class

Specifies that, when the string is rendered into an image, it will be italicized.

Syntax

public FontStyleBvr italic( );

Return Value

Returns the FontStyleBvr object.

size

FontStyleBvr Class

Specifies the size of the font, in points. Because this is a NumberBvr object, the size can vary over time. This parameter can also be of type double.

Syntax

public FontStyleBvr size( NumberBvr points );

Parameters

points
NumberBvr object specifying the size of the font. This parameter can also be of type double.

Return Value

Returns the FontStyleBvr object.

strikethrough

FontStyleBvr Class

Specifies that, when the string is rendered into an image, it will be struck through.

Syntax

public FontStyleBvr strikethrough( );

Return Value

Returns the FontStyleBvr object.

textAntialiasing

FontStyleBvr Class

Determines whether the text will be antialiased.

Syntax

public FontStyleBvr textAntialiasing( double antiAliasing );

Parameters

antiAliasing
A double value that specifies whether the text will be antialiased. A 0 (the default value) means there is no antialiasing and a 1 means there is.

Return Value

Returns the FontStyleBvr object.

underline

FontStyleBvr Class

Specifies that, when a string is rendered into an image, it will be underlined.

Syntax

public FontStyleBvr underline( );

Return Value

Returns the FontStyleBvr object.

weight

FontStyleBvr Class

Specifies the thickness of the boldness of the font. This attribute can be thought of as an animated bold. Note that the font itself will have specific, discrete settings for the different weights. Varying this value will not, therefore, result in a continuous change but in a series of discrete changes. The FontStyleBvr.bold method always overrides this setting.

Syntax

public FontStyleBvr weight( NumberBvr weight );

Parameters

weight
NumberBvr object that specifies the thickness of the boldness of the font. The value can range from 0 (the thinnest) to 1 (the thickest). This parameter can also be of type double.

Return Value

Returns the FontStyleBvr object.

newUninitBvr

FontStyleBvr Class

Enables you to refer to a FontStyleBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.)

Syntax

public static FontStyleBvr newUninitBvr( );

Return Value

Returns the FontStyleBvr object.


GeometryBvr Class-Java Class

GeometryBvr Class DirectAnimation Animated Header --GeometryBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: FontStyleBvr
  * Next Topic: ImageBvr

GeometryBvr Class

The GeometryBvr class is a class of three-dimensional objects that includes surfaces, lights, sounds, their attributes, and/or combinations of these elements. Like other behaviors, the value of the elements of the geometry can change over time.

In general, every geometry behavior is spatially infinite and of infinite resolution. Conceptually, each of the infinitely many points in the geometry has associated with it an opacity value, a color, a normal (used for lighting computations), a texture coordinate, a detectability value, and a single-channel sound emanating from that point (generally, this is silence).

Surface geometry is either detectable or not. If a surface is detectable, the geometry fires a pick event whenever the cursor passes over a surface point. Neither lights nor sounds are detectable (or pickable).

Light geometries include ambient lights, directional lights, point lights, and spotlights. Light geometries can be attributed with both color and distance attenuation.

A texture in a geometry is an image, the points of which are mapped to the surface of the three-dimensional object represented by the geometry. If there is no surface in the geometry, the texture is ignored.

A geometry can also be a sound. The sound is audible when the geometry is within the appropriate range of a microphone behavior.

The bounding box query is used to construct and compose media, and different types of images and behaviors provide specific bounding boxes. This means, for example, that the bounding box of an image is not defined as the smallest axis-aligned, enclosing region of the nontransparent parts of an image. Instead, bounding boxes are defined on a per-operation basis. The emptyGeometry object, for example, has its own definition of a bounding box, defined below.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to GeometryBvr Objects
  • Statics Fields Relevant to GeometryBvr Objects

GeometryBvr Methods

boundingBox | Creates a bounding box behavior that defines the axis-aligned bounding box enclosing all nontransparent portions of the geometry behavior.
diffuseColor | Specifies the diffuse color of the new geometry behavior. The diffuse color is the diffuse (or matte) reflective color of the surface.
lightAttenuation | Creates a new geometry behavior in which any light sources have the given attenuation.
lightColor | Creates a new geometry behavior in which all embedded lights have the specified color.
opacity | Sets the opacity value for all surfaces to the given number behavior.
render(camera) | Creates an image behavior that is a two-dimensional projection of this geometry behavior.
render(mic) | Creates a sound behavior that represents the sound associated with this geometry behavior as detected by the given microphone.
textureImage | Creates a new geometry behavior by mapping the given image, as defined by the given image behavior, to the surfaces of the original geometry.
transform | Creates a new geometry behavior that is the result of applying the given three-dimensional transformation to the original geometry behavior.
undetectable | Creates a new undetectable geometry behavior from the original geometry.
newUninitBvr | Enables you to refer to an GeometryBvr behavior before that behavior has been defined.

boundingBox

GeometryBvr Class

Creates a bounding box behavior that defines the axis-aligned bounding box enclosing all nontransparent portions of the geometry behavior.

Syntax

public Bbox3Bvr boundingBox( );

Return Value

Returns the Bbox3Bvr object.

diffuseColor

GeometryBvr Class

Specifies the diffuse color of the new geometry behavior. The diffuse color is the diffuse (or matte) reflective color of the surface.

Syntax

public GeometryBvr diffuseColor( ColorBvr col );

Parameters

col
ColorBvr object.

Return Value

Returns the GeometryBvr object.

Remarks

By default, the diffuse color is white. The bounding box is the bounding box of the underlying geometry.

This attribute overrides previous values.

lightAttenuation

GeometryBvr Class

Creates a new geometry behavior in which any light sources have the given attenuation. The new attenuation is set to 1 / (const + linear ´ d + quadratic ´ d2) where d is the distance from the light to the object.

Syntax

public GeometryBvr lightAttenuation(
NumberBvr const, NumberBvr linear, NumberBvr quadratic );

Parameters

const, linear, and quadratic
NumberBvr objects specifying the attenuation of a light source. These parameters can also be of type double.

Return Value

Returns the GeometryBvr object.

Remarks

By default, the attenuation coefficients are (1, 0, 0), which means no light attenuation. The bounding box is the bounding box of the underlying geometry.

An attenuation of (1, 1, 0) gives a linear attenuation that is useful for light falloff in a room. An attenuation of (1, 0, 1) gives a quadratic attenuation that simulates lights that are in the open, such as street lamps and headlights. This attribute overrides previous values.

lightColor

GeometryBvr Class

Creates a new geometry behavior in which all embedded lights have the specified color.

Syntax

public GeometryBvr lightColor( ColorBvr col );

Parameters

col
ColorBvr object.

Return Value

Returns the GeometryBvr object.

Remarks

By default, a light source is white. The bounding box is the bounding box of the underlying geometry.

This attribute overrides previous values.

opacity

GeometryBvr Class

Sets the opacity value for all surfaces to the given number behavior. Opacity values can range from 0.0 to 1.0.

Syntax

public GeometryBvr opacity( NumberBvr opac );

Parameters

opac
NumberBvr object representing the opacity value. The geometry is fully opaque if the value is 1, and fully transparent if the value is 0. This parameter can also be of type double.

Return Value

Returns the GeometryBvr object.

Remarks

If the opac parameter has a value outside the 0.0 to 1.0 range, the integer part of the value is discarded and only the fractional part is used. By default, the opacity is 1.

This attribute composes values. The resulting opacity is the new value times the old value.

render(camera)

GeometryBvr Class

Creates an image behavior that is a two-dimensional projection of this geometry behavior. The camera parameter defines how the geometry is projected into two dimensions.

Syntax

public ImageBvr render( CameraBvr camera );

Parameters

camera
CameraBvr object.

Return Value

Returns the ImageBvr object.

Remarks

The resulting image is infinite in extent. The bounding box of a rendered geometry is the axis-aligned extent of the projection of the three-dimensional bounding box onto the image plane.

render(mic)

GeometryBvr Class

Creates a sound behavior that represents the sound associated with this geometry behavior as detected by the given microphone.

Syntax

public SoundBvr render( MicrophoneBvr mic );

Parameters

mic
MicrophoneBvr object.

Return Value

Returns the SoundBvr object.

textureImage

GeometryBvr Class

Creates a new geometry behavior by mapping the given image, as defined by the given image behavior, to the surfaces of the original geometry.

This method replaces the earlier texture method. Texture continues to work for backward compatibility, but you should use textureImage, which defines the texture axes more accurately. Note that textureImage can be used only with versions of DirectAnimation 6.xx.xxx.xxx or later.

Syntax

public GeometryBvr textureImage( ImageBvr image );

Parameters

image
ImageBvr object.

Return Value

Returns the GeometryBvr object.

Remarks

If the geometry has no surfaces, the texture is ignored. If the original geometry already has a texture, the new texture completely overlays the original. Texture maps have no effect when applied to a geometry that lacks texture coordinates. By default, a geometry has no texture. The bounding box is the bounding box of the underlying geometry.

This attribute overrides previous values.

transform

GeometryBvr Class

Creates a new geometry behavior that is the result of applying the given three-dimensional transformation to the original geometry behavior.

Syntax

public GeometryBvr transform( Transform3Bvr xf );

Parameters

xf
Transform3Bvr object.

Return Value

Returns the GeometryBvr object.

Remarks

The transformation applies to all points in the geometry, including those associated with the positions and orientations of light sources, textures, and sounds. The bounding box of a transformed geometry is the axis-aligned box enclosing the box that is the result of applying the transformation to the underlying geometry.

This attribute composes values.

Example

The following example rotates a red cube around the y-axis by PI/3 radians:

//Get the cube (assume there is light)
GeometryBvr c0 = import("cube.x");
//Color it red
GeometryBvr c1 = c0.diffuseColor(red);
//Rotate it
GeometryBvr c2 = c1.transform(
rotate(yVector3, toBvr(Math.PI/3)));

undetectable

GeometryBvr Class

Creates a new undetectable geometry behavior from the original geometry. An undetectable geometry means that, while the geometry is still as visible or as audible as before, any attempts to pick it are ignored.

Syntax

public GeometryBvr undetectable( );

Return Value

Returns the GeometryBvr object.

Remarks

The undetectable method is useful for specifying which objects are detectable and which are not. For example, suppose a semitransparent green sphere is used to simulate a glow around an object. Because this sphere encloses the object, the object itself would be unpickable. Instead, the pick information would always be for the sphere. Tagging the sphere as unpickable allows the object itself to be picked. This attribute overrides previous values.

newUninitBvr

GeometryBvr Class

Enables you to refer to an GeometryBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static GeometryBvr newUninitBvr( );

Return Value

Returns the GeometryBvr object.


ImageBvr Class-Java Class

ImageBvr Class DirectAnimation Animated Header --ImageBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: GeometryBvr
  * Next Topic: JoinStyleBvr

ImageBvr Class

Creates an object that represents an image behavior. Much like a bitmap, an image is a two-dimensional picture composed of individual picture elements. Like other behaviors, however, the value of the picture elements can change over time.

Every image behavior consists of an infinite set of two-dimensional points, each representing a picture element and each having color, opacity, and detectability attributes. Colors in an image are defined using color behaviors. Opacity values are number behaviors, and can range in value from 0 through 1, with 0 for fully transparent and 1 for fully opaque. Only points that are "nontransparent" have any visible effect when the image is output or combined with other images.

Points in the image are either detectable or not. If a point is detectable, the image fires a pick event whenever the mouse passes over it.

The bounding box operation is used to construct and compose media, and different types of images and behaviors provide specific bounding boxes. This means, for example, that the bounding box of an image is not defined as the smallest axis-aligned, enclosing region of the nontransparent parts of an image. Instead, bounding boxes are defined on a per-operation basis. This means, for example, that the clipPolygon method has its own definition of a bounding box, defined below.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to ImageBvr Objects
  • Statics Fields Relevant to ImageBvr Objects

ImageBvr Methods

boundingBox | Creates a box behavior that defines the box that encloses all nontransparent portions of the image behavior.
clip | Creates a new image behavior by clipping the original image.
clipPolygon | Creates a new image behavior by clipping the original image.
crop | Creates a new image behavior by cropping the original image.
mapToUnitSquare | Creates an image that is mapped to the unit square.
opacity | Creates an image behavior in which the opacity value for each point is set to the given number behavior.
tile | Creates a new image behavior that consists of the original image tiled (repeated) in all directions.
transform | Creates a new image behavior that is the result of applying the given transformation to the points in the original image behavior.
undetectable | Creates a new undetectable image behavior from the original image.
newUninitBvr | Enables you to refer to an ImageBvr behavior before that behavior has been defined.

boundingBox

ImageBvr Class

Creates a box behavior that defines the box that encloses all nontransparent portions of the image behavior.

Syntax

public Bbox2Bvr boundingBox( );

Return Value

Returns the Bbox2Bvr object.

clip

ImageBvr Class

Creates a new image behavior by clipping the original image. The new image consists of that portion of the original image that is within the region(s) defined by the given matte behavior.

Syntax

public ImageBvr clip( MatteBvr matte );

Parameters

matte
MatteBvr object.

Return Value

Returns the ImageBvr object.

Remarks

The bounding box of an image clipped to a matte is the intersection of the matte's bounding box (the axis-aligned box that surrounds the matte), and the bounding box of the underlying image.

clipPolygon

ImageBvr Class

Creates a new image behavior by clipping the original image. The new image consists of that portion of the original image that is within the closed figure defined by the given point behaviors.

Syntax

public ImageBvr clipPolygon( Point2Bvr[] points );

Parameters

points
An array of Point2Bvr objects representing the endpoints of the line segments that define the figure. If the first and last points are not the same, these points are implicitly connected to close the figure.

Return Value

Returns the ImageBvr object.

Remarks

The bounding box is the intersection of the box surrounding the points of the polygon with the box of the underlying image.

crop

ImageBvr Class

Creates a new image behavior by cropping the original image. The new image consists of the portion of the original image that is within the region given by minPt and maxPt.

Syntax

public ImageBvr crop( Point2Bvr minPt, Point2Bvr maxPt );

Parameters

minPt and maxPt
Point2Bvr objects representing the lower-left and upper-right corners of the box enclosing the portion of the original image to use.

Return Value

Returns the ImageBvr object.

Remarks

The bounding box of a cropped image is the intersection of the crop box and the box of the underlying image.

mapToUnitSquare

ImageBvr Class

Creates an image that is mapped to the unit square. This is a square at coordinates [(0,0) (1,1)].

Syntax

public ImageBvr mapToUnitSquare( );

Remarks

This method is commonly used for images that will be textured because a geometry's texture coordinates generally span the unit square. It is also used for transforming an image into a space where it is easy to manipulate.

opacity

ImageBvr Class

Creates an image behavior in which the opacity value for each point is set to the given number behavior.

Syntax

public ImageBvr opacity( NumberBvr opac );

Parameters

opac
NumberBvr object representing the opacity value. The image is fully opaque if the value is 1, and fully transparent if the value is 0. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

Remarks

By default, an image's opacity is 1. The bounding box is simply the bounding box of the underlying image itself. Opacity values can range from 0.0 to 1.0. If the corresponding number behavior has a value outside this range, the integer part of the value is discarded and only the fractional part is used.

This attribute composes values. The resulting opacity is the new value times the old value.

tile

ImageBvr Class

Creates a new image behavior that consists of the original image tiled (repeated) in all directions. The bounding box is the infinite bounding box.

Syntax

public ImageBvr tile( );

Return Value

Returns the ImageBvr object.

transform

ImageBvr Class

Creates a new image behavior that is the result of applying the given transformation to the points in the original image behavior.

Syntax

public ImageBvr transform( Transform2Bvr xf );

Parameters

xf
Transform2Bvr object.

Return Value

Returns the ImageBvr object.

Remarks

As an example, you can double the size of the opaque portion of the image that is the result of applying a transformation that scales by 2. The bounding box of a transformed image is determined by applying the transform to the bounding box of the underlying image and then enclosing the resultant quadrilateral with an axis-aligned box.

This attribute composes values.

undetectable

ImageBvr Class

Creates a new undetectable image behavior from the original image.

Syntax

public ImageBvr undetectable( );

Return Value

Returns the ImageBvr object.

newUninitBvr

ImageBvr Class

Enables you to refer to an ImageBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static ImageBvr newUninitBvr( );

Return Value

Returns the ImageBvr object.


JoinStyleBvr Class-Java Class

JoinStyleBvr Class DirectAnimation Animated Header --JoinStyleBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ImageBvr
  * Next Topic: LineStyleBvr

JoinStyleBvr Class

Creates an object that represents a joinstyle behavior. The behavior defines the style (shape) of joint between intersecting line segments in a path. A joint is drawn only if some portions of the segments do not touch or overlap at the common endpoint. In this case, the outside corners of the line segments are connected, and the resulting enclosed area is filled with the same color as the line.

You use the endstyle behavior with the join method of the LineStyleBvr object.

For relevant fields from the Statics class, see Statics Fields Relevant to JoinStyleBvr Objects.

JoinStyleBvr Methods

newUninitBvr Enables you to refer to a JoinStyleBvr behavior before that behavior has been defined.

newUninitBvr

JoinStyleBvr Class

Enables you to refer to a JoinStyleBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static JoinStyleBvr newUninitBvr( );

Return Value

Returns the JoinStyleBvr object.


LineStyleBvr Class-Java Class

LineStyleBvr Class DirectAnimation Animated Header --LineStyleBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: JoinStyleBvr
  * Next Topic: MatteBvr

LineStyleBvr Class

Creates an object that represents a linestyle behavior. The behavior defines the various line styles to use when drawing lines. These styles include the shape of the end of a line, the shape of joints between intersecting lines, the width of the line, and whether the line is drawn with a continuous stroke or as a sequence of dashes.

You use linestyle behaviors with the draw method of the Path2Bvr class.

For relevant fields from the Statics class, see Statics Fields Relevant to LineStyleBvr Objects.

LineStyleBvr Methods

color | Determines the color of a LineStyleBvr object.
dash | Creates a linestyle behavior that is the result of applying the given dashstyle behavior to the existing linestyle.
detail | Creates a detail linestyle behavior from an existing linestyle.
end | Creates a linestyle behavior that is the result of applying the given endstyle to the existing linestyle.
join | Creates a linestyle behavior that is the result of applying the given joinstyle behavior to the existing linestyle.
lineAntialiasing | Determines whether the line will be antialiased.
width | Creates a linestyle behavior from an existing linestyle by setting the width of line to the given amount, expressed in points.
newUninitBvr | Enables you to refer to a LineStyleBvr behavior before that behavior has been defined.

color

LineStyleBvr Class

Determines the color of a LineStyleBvr object. The default color is black.

Syntax

public LineStyleBvr color( ColorBvr color );

Parameters

color
ColorBvr object that sets the color of the line. The default color is black.

Return Value

Returns the LineStyleBvr object.

dash

LineStyleBvr Class

Creates a linestyle behavior that is the result of applying the given dashstyle behavior to the existing linestyle. By default, the dashstyle is solid.

Syntax

public LineStyleBvr dash( DashStyleBvr ds );

Parameters

ds
DashStyleBvr object that represents the dashstyle behavior to apply.

Return Value

Returns the LineStyleBvr object.

detail

LineStyleBvr Class

Creates a detail linestyle behavior from an existing linestyle. A detail linestyle is not changed by image scaling operations. By default, the detail linestyle is on. This means it is the default style and it has no width.

Syntax

public LineStyleBvr detail( );

Return Value

Returns the LineStyleBvr object.

Remarks

Any width or any endstyle or joinstyle behavior associated with the existing linestyle is discarded when creating the new line style.

end

LineStyleBvr Class

Creates a linestyle behavior that is the result of applying the given endstyle to the existing linestyle. By default, the endstyle is flat.

Syntax

public LineStyleBvr end( EndStyleBvr es );

Parameters

es
EndStyleBvr object that represents the endstyle behavior to apply.

Return Value

Returns the LineStyleBvr object.

join

LineStyleBvr Class

Creates a linestyle behavior that is the result of applying the given joinstyle behavior to the existing linestyle. By default, the joinstyle is beveled.

Syntax

public LineStyleBvr join( JoinStyleBvr js );

Parameters

js
JoinStyleBvr object that represents the joinstyle behavior to apply.

Return Value

Returns the LineStyleBvr object.

lineAntialiasing

LineStyleBvr Class

Determines whether the line will be antialiased.

Syntax

public LineStyleBvr lineAntialiasing( double antiAliasing );

Parameters

antiAliasing
A double value that specifies whether or not there will be antialiasing. A 0 (the default value) means there is no antialiasing and a 1 means there is.

Return Value

Returns the FontStyleBvr object.

width

LineStyleBvr Class

Creates a linestyle behavior from an existing linestyle by setting the width of line to the given amount, expressed in points. If the result is a line less than 1 pixel wide, the line will always be a detail line.

Syntax

public LineStyleBvr width( NumberBvr width );

Parameters

width
NumberBvr object that represents the width of the line, in points. This parameter can also be of type double.

Return Value

Returns the LineStyleBvr object.

Remarks

This attribute overrides detail.

newUninitBvr

LineStyleBvr Class

Enables you to refer to a LineStyleBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static LineStyleBvr newUninitBvr( );

Return Value

Returns the LineStyleBvr object.


MatteBvr Class-Java Class

MatteBvr Class DirectAnimation Animated Header --MatteBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: LineStyleBvr
  * Next Topic: MicrophoneBvr

MatteBvr Class

Creates an object representing a matte behavior. A matte consists of a path and/or text, and is typically used to clip an image to the shape defined by the path and/or text.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to MatteBvr Objects
  • Statics Fields Relevant to MatteBvr Objects

MatteBvr Methods

transform | Creates a new matte behavior that is the result of applying the given transformation behavior to the original matte.
newUninitBvr | Enables you to refer to a MatteBvr behavior before that behavior has been defined.

transform

MatteBvr Class

Creates a new matte behavior that is the result of applying the given transformation behavior to the original matte. The transformation affects each point behavior in the path that defines the matte.

Syntax

public MatteBvr transform( Transform2Bvr xf );

Parameters

xf
Transform2Bvr object.

Return Value

Returns the MatteBvr object.

Remarks

This attribute composes values.

newUninitBvr

MatteBvr Class

Enables you to refer to a MatteBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static MatteBvr newUninitBvr( );

Return Value

Returns the MatteBvr object.


MicrophoneBvr Class-Java Class

MicrophoneBvr Class DirectAnimation Animated Header --MicrophoneBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: MatteBvr
  * Next Topic: Model

MicrophoneBvr Class

Creates an object that represents a microphone behavior. A microphone is used with the render method to produce spatialized sound behaviors from sounds embedded in a geometry.

Every microphone has a location and an orientation in a three-dimensional space, and this location determines how a sound is detected. The closer a microphone is to the source of a sound, the louder and sooner the microphone detects that sound. The effect is to "spatialize" the sound, that is, make it seem that it was actually recorded in a three-dimensional space.

For the default microphone, the location is the origin, looking in the negative z-direction, with positive y considered up. You can create new microphones at different locations by using the transform method. Microphones are omnidirectional.

For more information about behaviors, see the Behavior class.

For relevant fields from the Statics class, see Statics Fields Relevant to MicrophoneBvr Objects.

MicrophoneBvr Methods

transform | Creates a new microphone behavior that is the result of applying the given transformation to the original microphone's location.
newUninitBvr | Enables you to refer to a MicrophoneBvr behavior before that behavior has been defined.

transform

MicrophoneBvr Class

Creates a new microphone behavior that is the result of applying the given transformation to the original microphone's location.

Syntax

public MicrophoneBvr transform( Transform3Bvr xf );

Parameters

xf
Transform3Bvr object.

Return Value

Returns the MicrophoneBvr object.

Remarks

This attribute composes values.

newUninitBvr

MicrophoneBvr Class

Enables you to refer to a MicrophoneBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static MicrophoneBvr newUninitBvr( );

Return Value

Returns the MicrophoneBvr object.


Model Class-Java Class

Model Class DirectAnimation Animated Header --Model Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: MicrophoneBvr
  * Next Topic: ModelMakerApplet

Model Class

The model is the set of behaviors actually sampled and displayed for an application. An instance of the Model class must be provided when constructing a Microsoft® DirectAnimation™ viewer to actually view a behavior. The Model class can also optionally set some model-specific preferences, such as rendering quality. The following example uses several methods included in the Model class to display the familiar phrase, "Hello, World."

class MyModel extends Model {
    // Create the behaviors that will be displayed
    public void createModel(BvrsToRun extraBvrsToRun) {
        FontStyleBvr fs =
        ImageBvr tx = (toBvr("Hello, World"), fs);

        // set the image
        setImage(tx);
    }
}

For more information about behaviors, see the Behavior class.

Model Methods

createModel | Builds the behaviors to be viewed.
getImage | Retrieves the model's image.
Model | Creates a Model object.
getImportBase | Retrieves the current base to use for constructing URLs.
getSound | Retrieves the model's sound.
setImage | Establishes the image behavior displayed by DirectAnimation when this model is displayed.
setImportBase | Sets the default base URL for importing media files.
setSound | Establishes the sound behavior displayed by DirectAnimation when this model is displayed.

createModel

Model Class

Builds the behaviors to be viewed.

Syntax

public abstract void createModel( BvrsToRun extraBvrsToRun );

Parameters

extraBvrsToRun
Behaviors that are not part of the model.

Remarks

Behaviors are run as soon as the model is started, and are all started at time = startTime. The developer implements this method, constructs the behaviors, sets them in the model with setImage and setSound, and returns. It is up to the DirectAnimation system to invoke this method.

getImage

Model Class

Retrieves the model's image.

Syntax

public ImageBvr getImage( );

Return Value

Returns the ImageBvr object.

Model

Model Class

Creates a Model object.

Syntax

public Model( );

getImportBase

Model Class

Retrieves the current base to use for constructing URLs. If imports occur outside of an instance of Model, the base must be passed.

Syntax

public URL getImportBase( );

Return Value

Returns the base URL of the image. For more information about java.net.URL objects, consult a Java reference.

Remarks

All paths should be fully qualified rather than relative. For example, instead of using:

//This is wrong
importImage("../im1.jpg")

use:

//This is right
importImage(new URL(getImportBase(), "../im1.jpg"));

getSound

Model Class

Retrieves the model's sound.

Syntax

public SoundBvr getSound( );

Return Value

Returns the SoundBvr object.

setImage

Model Class

Establishes the image behavior displayed by DirectAnimation when this model is displayed. Typically called by the model inside of the createModel method provided by the application.

Syntax

public void setImage( ImageBvr im1 );

Parameters

im
ImageBvr object.

Remarks

See the ImageBvr class for information about image behaviors.

setImportBase

Model Class

Sets the default base URL for importing media files. Typically called in the DXMApplet and DXMCanvas classes to set the default base. It can also be explicitly used by the developer to set the default base.

Syntax

public void setImportBase( URL url );

Parameters

url
Default base URL. For more information about java.net.URL objects, consult a Java reference.

See Also

getImportBase

setSound

Model Class

Establishes the sound behavior displayed by DirectAnimation when this model is displayed. Typically called by the model inside of the createModel method provided by the application.

Syntax

public void setSound( SoundBvr snd );

Parameters

snd
SoundBvr object.

Remarks

See the SoundBvr class for information about sound behaviors.


ModelMakerApplet Class-Java Class

ModelMakerApplet Class DirectAnimation Animated Header --ModelMakerApplet Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Model
  * Next Topic: ModifiableBehavior

ModelMakerApplet Class

The ModelMakerApplet class is used for constructing models in Java and using them in windowless controls. It is similar to DXMApplet except that it exposes some COM interfaces and doesn't do any ticking (this is performed by the Microsoft® DirectAnimation™ control called DAViewer). Here is the basic syntax for using ModelMakerApplet:

public class MyTest extends ModelMakerApplet{
    public MyTest() {
        setModel(new testModel());
    }
}

The grabBackgroundImageComPtr, grabImageComPtr, grabSoundComPtr, and grabViewComPtr methods are used on the HTML page and can be called either from Microsoft® Visual Basic® Scripting Edition (VBScript) or Microsoft® JScript®. Here is an example of an HTML page that calls these methods, using VBScript:

<HTML>
<HEAD>
<TITLE>myTest Windowless Control</TITLE>
</HEAD>

<body>

<DIV ID=LAYOUT STYLE="position:absolute;
left:0; top:0; width:600; height:400">

<DIV ID=text STYLE="position:absolute;
left:0; top:0; width:450; height:280">
<FONT SIZE=4 FACE="Verdana,Tahoma,Arial">

' This is standard HTML arranged using DIV tags, and atop it we're
' going to place our Microsoft® DirectX® Animation windowless control.

</FONT></DIV>
</DIV>

<DIV ID=controlDiv>
<OBJECT ID="DAViewer"
    STYLE="position:absolute;left:50;top:10;width:200;height:300"
    CLASSID="CLSID:B6FFC24C-7E13-11D0-9B47-00C04FC2F51D">
</OBJECT>
</DIV>

</DIV>

' The width and height parameters are placeholders. They need to
' be there to specify the applet and must be set to 1.

<applet code=myTest.class codebase=. width=1 height=1 id=javaApp>
</applet>

<script language="VBScript">
    sub window_onLoad
        set a = document.javaApp

        ' Get model components out of the Java applet and give it
        ' to the control.
        DAViewer.View            = a.grabViewComPtr
        DAViewer.Image           = a.grabImageComPtr
        DAViewer.BackgroundImage = a.grabBackgroundImageComPtr
        DAViewer.Sound           = a.grabSoundComPtr

        ' Start the control viewing
        DAViewer.Start

    end sub
</script>

</BODY>
</HTML>

ModelMakerApplet Methods

createBackgroundImage | Used so that models constructed in Java, intended for windowless controls, will have a background image if they appear in a browser that does not support this feature.
grabBackgroundImageComPtr | Retrieves the background image component of the model.
grabImageComPtr | Retrieves the image component of the model.
grabSoundComPtr | Retrieves the sound component of the model.
grabViewComPtr | Retrieves the viewer component of the model.
ModelMakerApplet | Creates a ModelMakerApplet object.
registerErrorAndWarningReceiver | Enables the application to register an object that will have its methods invoked upon asynchronous errors encountered when DirectAnimation media is controlling the frame loop or when recoverable errors occur when sampling and displaying the model.
setModel | Sets the model that will be used.

createBackgroundImage

ModelMakerApplet Class

Used so that models constructed in Java, intended for windowless controls, will have a background image if they appear in a browser that does not support this feature. The model will then be displayed in its own window, as if it were in a standard DirectAnimation applet. Just as these models need a background image to be explicitly inserted, so do windowless applets if they are to degrade gracefully to a windowed display.

Syntax

public ImageBvr createBackgroundImage( );

Return Value

Returns the ImageBvr object.

Remarks

This method should be called within ModelMakerApplet and not during the createModel implementation. Here is an example:

public class myTest extends ModelMakerApplet {

    public ImageBvr createBackgroundImage() {
        return Statics.solidColorImage(Statics.black);
    }
        setModel(new testModel());
}

grabBackgroundImageComPtr

ModelMakerApplet Class

Retrieves the background image component of the model. This method is called from a script on the HTML page.

Syntax

public IDABehavior grabBackgroundImageComPtr( );

Return Value

Returns the COM pointer to the IDABehavior interface (rather than an ImageBvr.)

grabImageComPtr

ModelMakerApplet Class

Retrieves the image component of the model. This method is called from a script on the HTML page.

Syntax

public IDABehavior grabImageComPtr( );

Return Value

Returns the COM pointer to the IDABehavior interface (rather than an ImageBvr.)

grabSoundComPtr

ModelMakerApplet Class

Retrieves the sound component of the model. This method is called from a script on the HTML page.

Syntax

public IDABehavior grabSoundComPtr( );

Return Value

Returns the COM pointer to the IDABehavior interface (rather than a SoundBvr.)

grabViewComPtr

ModelMakerApplet Class

Retrieves the viewer component of the model. This method is called from a script on the HTML page.

Syntax

public IDAView grabViewComPtr( );

Return Value

Returns the COM pointer to the IDABehavior interface (rather than a Viewer.)

ModelMakerApplet

ModelMakerApplet Class

Creates a ModelMakerApplet object.

Syntax

public ModelMakerApplet( );

registerErrorAndWarningReceiver

ModelMakerApplet Class

Enables the application to register an object that will have its methods invoked upon asynchronous errors encountered when DirectAnimation media is controlling the frame loop or when recoverable errors occur when sampling and displaying the model.

Syntax

public ErrorAndWarningReceiver registerErrorAndWarningReceiver(
ErrorAndWarningReceiver w );

Parameters

w
ErrorAndWarningReceiver object to be registered.

Return Value

Returns the ErrorAndWarningReceiver object.

setModel

ModelMakerApplet Class

Sets the model that will be used.

Syntax

public synchronized void setModel( Model model );

Parameters

model
Model to be set.

Remarks

This method must be called before the model is started. Calling it after the model has started causes an exception.


ModifiableBehavior Class-Java Class

ModifiableBehavior Class DirectAnimation Animated Header --ModifiableBehavior Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ModelMakerApplet
  * Next Topic: MontageBvr

ModifiableBehavior Class

Provides an asynchronous method of modifying behaviors, often in response to externally generated application events. It is instanced with the new operator, is given an initial behavior, and results in a new behavior for the caller to use. Here is an example:

//...Create a solid color image somewhere in the program.
//It will have a switchable color.
ModifiableBehavior sw = new ModifiableBehavior(red);
ColorBvr col = (ColorBvr)(sw.getBvr());
ImageBvr im = solidColorImage(col);

//...somewhere else in the program...
sw.switchTo(blue);

Anywhere that col was used will turn from red to blue. In this example, sw contains an untyped Behavior which the example explicitly casts to a ColorBvr. This is necessary because ColorBvr is the type provided as the input to the construction of sw. Calls to switchTo must carry arguments of the same run-time behavior type as the one with which sw was constructed. The replacement behavior begins as soon after the transition occurs as is possible.

ModifiableBehavior Methods

getBvr | Extracts the Behavior component of the ModifiableBehavior object.
ModifiableBehavior | Use the new operator to construct an instance of the ModifiableBehavior class.
switchTo | When invoked, replaces the behavior generated by getBvr with the provided behavior.

getBvr

ModifiableBehavior Class

Extracts the Behavior component of the ModifiableBehavior object.

Syntax

public Behavior getBvr( );

Return Value

Returns the Behavior object.

Remarks

This behavior is subject to the switchTo invocations on the ModifiableBehavior object itself.

ModifiableBehavior

ModifiableBehavior Class

Use the new operator to construct an instance of the ModifiableBehavior class. Supply an initial behavior and a behavior context.

Syntax

public ModifiableBehavior( Behavior initialBvr );

Parameters

initialBvr
Initial behavior.

switchTo

ModifiableBehavior Class

When invoked, replaces the behavior generated by getBvr with the provided behavior.

Syntax

public void switchTo( Behavior newBvr );

Parameters

newBvr
Behavior that is begun once the switchTo method is invoked.

Remarks

Calls to this method must carry arguments of the same run-time behavior type as the one with which the ModifiableBehavior object was constructed. This is an immediate method, which means the new behavior is started when the transition occurs.


MontageBvr Class-Java Class

MontageBvr Class DirectAnimation Animated Header --MontageBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ModifiableBehavior
  * Next Topic: NumberBvr

MontageBvr Class

A MontageBvr is an object that represents a montage behavior. A montage is a stack of images in which each image has an explicit depth. Images with larger z-values are always in front of images with smaller z-values. Rendering a montage creates a new image behavior that is a composite of the images in the montage. The composite is created by overlaying the montage's images by order of depth and applying the same overlay rules as defined by the overlay method.

Because the depth of an image in a montage can vary over time, rendering the montage at different points in time can produce different images in different orders.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to MontageBvr Objects
  • Statics Fields Relevant to MontageBvr Objects

MontageBvr Methods

render | Creates an image behavior that consists of a composite of all images in the montage behavior.
newUninitBvr | Enables you to refer to a MontageBvr behavior before that behavior has been defined.

render

MontageBvr Class

Creates an image behavior that consists of a composite of all images in the montage behavior.

Syntax

public ImageBvr render( )

Return Value

Returns the ImageBvr object.

Remarks

The composite is created by overlaying the montage's images in order of Z-values, with the largest Z-value on top, and applying the same overlay rules as defined by the overlay method. If two images have the same depth, they are layered on top of each other in the order in which they were added to the montage. The bounding box of a rendered montage is simply the union of the bounding boxes of all the images comprising that montage.

newUninitBvr

MontageBvr Class

Enables you to refer to a MontageBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static MontageBvr newUninitBvr( );

Return Value

Returns the MontageBvr object.


NumberBvr Class-Java Class

NumberBvr Class DirectAnimation Animated Header --NumberBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: MontageBvr
  * Next Topic: PairObject

NumberBvr Class

Creates an object that represents a number behavior. At any given time, the value of the behavior is a floating point number.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to NumberBvr Objects
  • Statics Fields Relevant to NumberBvr Objects

NumberBvr Methods

extract | Extracts the value of a behavior, returning a Java object.
toString(double) | Converts a double to a StringBvr.
toString(NumberBvr) | Converts a NumberBvr to a StringBvr.
newUninitBvr | Enables you to refer to a NumberBvr behavior before that behavior has been defined.

extract

NumberBvr Class

Extracts the value of the behavior, returning a Java object. The behavior must have a constant value.

Syntax

public Object extract( );

Return Value

Returns a Java object. For more information about java.lang.Object objects, consult a Java reference.

toString(double)

NumberBvr Class

Converts a double to a StringBvr.

Syntax

public StringBvr toString( double a );

Parameters

a
The double value representing the number of digits to use after the decimal point.

Return Value

Returns the StringBvr object.

toString(NumberBvr)

NumberBvr Class

Converts a NumberBvr to a StringBvr.

Syntax

public StringBvr toString( NumberBvr a );

Parameters

a
NumberBvr object representing the number of digits to use after the decimal point.

Return Value

Returns the StringBvr object.

newUninitBvr

NumberBvr Class

Enables you to refer to a NumberBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static NumberBvr newUninitBvr( );

Return Value

Returns the NumberBvr object.


PairObject Class-Java Class

PairObject Class DirectAnimation Animated Header --PairObject Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: NumberBvr
  * Next Topic: Path2Bvr

PairObject Class

Used in conjunction with the andEvent and the getPickEvent methods. The andEvent method creates a third event from two simultaneously occurring events. It returns a PairObject whose first member is the event data returned by the first event and whose second member is the data returned by the second event. The getPickEvent method returns a PairObject whose first member is the point of intersection and whose second member is a vector that is perpendicular to the surface. Note that, unlike arrays, a PairObject can contain two objects of different types.

PairObject Methods

getFirst | Retrieves the first object of the PairObject.
getSecond | Retrieves the second object of the PairObject.
PairObject | Constructs a PairObject object.

getFirst

PairObject Class

Retrieves the first object of the PairObject.

Syntax

public Object getFirst( );

Return Value

Returns the object that is the first member of the PairObject.

getSecond

PairObject Class

Retrieves the second object of the PairObject.

Syntax

public Object getSecond( );

Return Value

Returns the object that is the second member of the PairObject.

PairObject

PairObject Class

Constructs a PairObject object. Note that this is not a behavior.

Syntax

public PairObject( Object a, Object b );

Parameters

a
First member of the PairObject.
b
Second member of the PairObject.


Path2Bvr Class-Java Class

Path2Bvr Class DirectAnimation Animated Header --Path2Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: PairObject
  * Next Topic: PickableGeometry

Path2Bvr Class

Creates an object that represents a path behavior for two-dimensional space. A path behavior consists of a sequence of two-dimensional point behaviors and is typically used to draw images consisting of line segments or to define borders for geometric shapes.

For more information about behaviors, see the Behavior class.

For relevant methods from the Statics class, see Statics Methods Relevant to Path2Bvr Objects.

Path2Bvr Methods

boundingBox | Creates a Path2Bvr object that is a visible bounding box.
close | Adds a line segment from the end of the path to the start. This method is useful for animating closed regions.
draw | Renders the Path2Bvr object into an ImageBvr object. The image is unfilled, which means only the outline is rendered.
fill | Renders the Path2Bvr object into an ImageBvr object.
transform | Creates a new path behavior that is the result of applying the given transformation behavior to the points in the original path behavior.
newUninitBvr | Enables you to refer to a Path2Bvr behavior before that behavior has been defined.

boundingBox

Path2Bvr Class

Creates a Path2Bvr object that is a visible bounding box.

Syntax

public Bbox2Bvr boundingBox( LineStyleBvr style );

Parameters

style
LineStyleBvr object that defines the line style of the path. This argument is ignored. The linestyle is always the default linestyle and has a width of 0.

Return Value

Returns the Path2Bvr object.

close

Path2Bvr Class

Adds a line segment from the end of the path to the start. This method is useful for animating closed regions.

Syntax

public Path2Bvr close( );

Return Value

Returns the Path2Bvr object.

draw

Path2Bvr Class

Renders the Path2Bvr object into an ImageBvr object. The image is unfilled, which means only the outline is rendered.

Syntax

public ImageBvr draw( LineStyleBvr ls );

Parameters

ls
LineStyleBvr used to render the image.

Return Value

Returns the ImageBvr object.

fill

Path2Bvr Class

Renders the Path2Bvr object into an ImageBvr object. The ls argument specifies the LineStyleBvr that is used to draw the outline. The im parameter specifies the image used to fill in the new ImageBvr object.

Syntax

public ImageBvr fill( LineStyleBvr ls, ImageBvr im );

Parameters

ls
LineStyleBvr object used to draw the path.
im
ImageBvr object used as the fill pattern.

Return Value

Returns the ImageBvr object.

transform

Path2Bvr Class

Creates a new path behavior that is the result of applying the given transformation behavior to the points in the original path behavior.

Syntax

public Path2Bvr transform( Transform2Bvr xf );

Parameters

xf
Transform2Bvr object representing the transformation to apply to the original path.

Return Value

Returns the Path2Bvr object.

Remarks

This attribute composes values.

newUninitBvr

Path2Bvr Class

Enables you to refer to a Path2Bvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Path2Bvr newUninitBvr( );

Return Value

Returns the Path2Bvr object.


PickableGeometry Class-Java Class

PickableGeometry Class DirectAnimation Animated Header --PickableGeometry Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Path2Bvr
  * Next Topic: PickableImage

PickableGeometry Class

Creates an object representing a pickable geometry behavior. The behavior is identical to a geometry behavior except that an event occurs whenever the mouse pointer is "in" the geometry.

Applets typically use the event returned by the getPickEvent method to set an event handler or to create a reactive behavior with the until or untilNotify method.

When the pick event occurs, the event data is a PairObject object. The first element is a Point3Bvr object representing the intersection point, and the second element is a Vector3Bvr object that is a time-varying vector. The vector gives the model-coordinate offset to the point that is in the plane perpendicular to the viewing direction (from the camera's image plane), and is under the two-dimensional mouse's location.

PickableGeometry Methods

getGeometryBvr | Retrieves the GeometryBvr object.
getPickEvent | Creates an event for the pickable geometry behavior.
PickableGeometry | Creates a pickable GeometryBvr object.

getGeometryBvr

PickableGeometry Class

Retrieves the GeometryBvr object.

Syntax

public GeometryBvr getGeometryBvr( );

Return Value

Returns the GeometryBvr object.

getPickEvent

PickableGeometry Class

Creates an event for the pickable geometry behavior.

Syntax

public DXMEvent getPickEvent( );

Return Value

Returns a DXMEvent object. Its data is a PairObject. The first member is the intersection point that is in the plane perpendicular to the viewing direction and is under the mouse. The second member is a time-varying vector that gives the model-coordinate offset to the intersection point. Each sample taken while the mouse is over the object returns a new point and a new time-varying vector. Each time-varying vector tracks the mouse, relative to the associated intersection point.

Remarks

Use this event to set an event handler or create a reactive behavior with the until or untilNotify method.

PickableGeometry

PickableGeometry Class

Creates a pickable GeometryBvr object.

Syntax

public PickableGeometry( GeometryBvr geo );

Parameters

geo
New instance of a GeometryBvr that is pickable.


PickableImage Class-Java Class

PickableImage Class DirectAnimation Animated Header --PickableImage Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: PickableGeometry
  * Next Topic: Point2Bvr

PickableImage Class

Creates an object representing a pickable image behavior. The behavior is identical to an image behavior except that an event occurs whenever the mouse pointer is "in" the image.

Applets typically use the event returned by the getPickEvent method to set an event handler or to create a reactive behavior with the until or untilNotify method.

When the pick event occurs, the event data is a DXMEvent object. The first element is a Point2Bvr object representing the intersection point, and the second element is a Vector2Bvr object that is perpendicular to the surface.

PickableImage Methods

getImageBvr | Retrieves the ImageBvr object.
getPickEvent | Creates an event for the pickable image behavior.
PickableImage | Creates a pickable ImageBvr object that, depending on the value of bool, either is or is not pickable when there are other images between it and the mouse.

getImageBvr

PickableImage Class

Retrieves the ImageBvr object.

Syntax

public ImageBvr getImageBvr( );

Return Value

Returns the ImageBvr object.

getPickEvent

PickableImage Class

Creates an event for the pickable image behavior.

Syntax

public DXMEvent getPickEvent( );

Return Value

Returns a DXMEvent object. Its data is a PairObject. The first member is the intersection point that is in the plane perpendicular to the viewing direction and is under the mouse. The second member is a time-varying vector that gives the model-coordinate offset to the intersection point. Each sample taken while the mouse is over the object returns a new point and a new time-varying vector. Each time-varying vector tracks the mouse, relative to the associated intersection point.

Remarks

Use this event to set an event handler or create a reactive behavior with the until or untilNotify method.

PickableImage

PickableImage Class

Creates a pickable ImageBvr object that, depending on the value of bool, either is or is not pickable when there are other images between it and the mouse.

Syntax

public PickableImage( ImageBvr im, bool );

Parameters

im
New instance of the ImageBvr that is pickable.
bool
Determines whether the image is pickable where occluded. (This means that the pickable image will still get the pick event even if the mouse is on an occluding image.) The default is false, which means the image is not pickable where occluded. It isn't necessary to include the false value in the arguments. If true, the image is pickable where occluded. The true value must be included in the argument list.


Point2Bvr Class-Java Class

Point2Bvr Class DirectAnimation Animated Header --Point2Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: PickableImage
  * Next Topic: Point3Bvr

Point2Bvr Class

Creates an object that represents a two-dimensional point behavior. At any given time, the value of the behavior is a point, representing a location in a plane or on the screen. Each point has a pair of coordinate values that define the point's location relative to a fixed point called the origin. These coordinate values are given as either Cartesian coordinates (x,y) or polar coordinates (XY angle,length) when the point behavior is created.

Because coordinate values are number behaviors (NumberBvr objects), the location of a point behavior may change over time as the number behaviors change. For more information about behaviors, see the Behavior class and the Point3Bvr class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to Path2Bvr Objects
  • Statics Fields Relevant to Point2Bvr Objects

Point2Bvr Methods

getPolarCoordAngle | Creates a number behavior that represents the counter-clockwise angular displacement, in radians, relative to the positive x-axis, of the point behavior.
getPolarCoordLength | Creates a number behavior that represents the length (or magnitude) of the vector.
getX | Creates a number behavior that represents the x-coordinate of this point behavior.
getY | Creates a number behavior that represents the y-coordinate of this point behavior.
transform | Creates a new two-dimensional point behavior that is the result of applying the given transformation to the original point behavior.
newUninitBvr | Enables you to refer to a Point2Bvr behavior before that behavior has been defined.

getPolarCoordAngle

Point2Bvr Class

Creates a number behavior that represents the counter-clockwise angular displacement, in radians, relative to the positive x-axis, of the point behavior. The range is [-Pi, +Pi].

Syntax

public NumberBvr getPolarCoordAngle( );

Return Value

Returns the NumberBvr object.

See Also

getPolarCoordLength, point2Polar

getPolarCoordLength

Point2Bvr Class

Creates a number behavior that represents the length (or magnitude) of the vector.

Syntax

public NumberBvr getPolarCoordLength( );

Return Value

Returns the NumberBvr object.

See Also

getPolarCoordAngle, point2Polar

getX

Point2Bvr Class

Creates a number behavior that represents the x-coordinate of this point behavior.

Syntax

public NumberBvr getX( );

Return Value

Returns the NumberBvr object.

See Also

getY, point2

getY

Point2Bvr Class

Creates a number behavior that represents the y-coordinate of this point behavior.

Syntax

public NumberBvr getY( );

Return Value

Returns the NumberBvr object.

See Also

getX, point2

transform

Point2Bvr Class

Creates a new two-dimensional point behavior that is the result of applying the given transformation to the original point behavior.

Syntax

public Point2Bvr transform( Transform2Bvr xf );

Parameters

xf
Transform2Bvr object.

Return Value

Returns the Point2Bvr object.

Remarks

This attribute composes values.

newUninitBvr

Point2Bvr Class

Enables you to refer to a Point2Bvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Point2Bvr newUninitBvr( );

Return Value

Returns the Point2Bvr object.


Point3Bvr Class-Java Class

Point3Bvr Class DirectAnimation Animated Header --Point3Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Point2Bvr
  * Next Topic: Preferences

Point3Bvr Class

Creates an object that represents a three-dimensional point behavior. At any given time, the value of the behavior is a point, representing a location in a three-dimensional space. Each point has a triplet of coordinate values that define the point's location relative to a fixed point called the origin. These coordinate values are given as either Cartesian coordinates (x, y, z) or spherical coordinates (XY angle, YZ angle, length) when the point behavior is created.

Because coordinate values are number behaviors (NumberBvr objects), the location of a point behavior may change over time as the number behaviors change. For more information about behaviors, see the Behavior class and the Point2Bvr class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to Point3Bvr Objects
  • Statics Fields Relevant to Point3Bvr Objects

Point3Bvr Methods

getSphericalCoordLength | Retrieves the NumberBvr object that represents the length (or magnitude) of the vector.
getSphericalCoordXYAngle | Retrieves the NumberBvr object that should more properly be thought of as an XZ angle.
getSphericalCoordYZAngle | Retrieves the NumberBvr object that should more properly be thought of as an XY angle.
getX | Creates a number behavior that represents the x-coordinate of this point behavior.
getY | Creates a number behavior that represents the y-coordinate of this point behavior.
getZ | Creates a number behavior that represents the z-coordinate of this point behavior.
project | Retrieves the two-dimensional point behavior of the projection of the three-dimensional point onto the image plane, via the given camera behavior.
transform | Creates a new three-dimensional point behavior that is the result of applying the given transformation to the original point behavior.
newUninitBvr | Enables you to refer to a Point3Bvr behavior before that behavior has been defined.

getSphericalCoordLength

Point3Bvr Class

Retrieves the NumberBvr object that represents the length (or magnitude) of the vector.

Syntax

public NumberBvr getSphericalCoordLength( );

Return Value

Returns the NumberBvr object.

See Also

getSphericalCoordXYAngle, getSphericalCoordYZAngle, point3Spherical

getSphericalCoordXYAngle

Point3Bvr Class

Retrieves the NumberBvr object that should more properly be thought of as an XZ angle. For positive values, it is the counter-clockwise angle, in radians, of the point in the XZ plane. When the angle is 0, the point lies on the z-axis. The range is [-Pi, +Pi].

Syntax

public NumberBvr getSphericalCoordXYAngle( );

Return Value

Returns the NumberBvr object.

See Also

getSphericalCoordYZAngle, getSphericalCoordLength, point3Spherical

getSphericalCoordYZAngle

Point3Bvr Class

Retrieves the NumberBvr object that should more properly be thought of as an XY angle. For positive values, it is the counter-clockwise angle, in radians, going from the XZ-plane towards the positive y-ray. When the angle is 0, the point lies on the x-axis. The range is [-Pi/2, +Pi/2].

Syntax

public NumberBvr getSphericalCoordYZAngle( );

Return Value

Returns the NumberBvr object.

See Also

getSphericalCoordXYAngle, getSphericalCoordLength, point3Spherical

getX

Point3Bvr Class

Creates a number behavior that represents the x-coordinate of this point behavior.

Syntax

public NumberBvr getX( );

Return Value

Returns the NumberBvr object.

See Also

getY, getZ, point3

getY

Point3Bvr Class

Creates a number behavior that represents the y-coordinate of this point behavior.

Syntax

public NumberBvr getY( );

Return Value

Returns the NumberBvr object.

See Also

getX, getZ, point3

getZ

Point3Bvr Class

Creates a number behavior that represents the z-coordinate of this point behavior.

Syntax

public NumberBvr getZ( );

Return Value

Returns the NumberBvr object.

See Also

getX, getY, point3

project

Point3Bvr Class

Retrieves the two-dimensional point behavior of the projection of the three-dimensional point onto the image plane, via the given camera behavior.

Syntax

public Point2Bvr project( CameraBvr camera );

Parameters

camera
CameraBvr object projecting the three-dimensional point onto the image plane.

Return Value

Returns the Point2Bvr object.

transform

Point3Bvr Class

Creates a new three-dimensional point behavior that is the result of applying the given transformation to the original point behavior.

Syntax

public Point3Bvr transform( Transform3Bvr xf );

Parameters

xf
Transform3Bvr object.

Return Value

Returns the Point3Bvr object.

Remarks

This attribute composes values.

newUninitBvr

Point3Bvr Class

Enables you to refer to a Point3Bvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Point3Bvr newUninitBvr( );

Return Value

Returns the Point3Bvr object.


Preferences Class-Java Class

Preferences Class DirectAnimation Animated Header --Preferences Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Point3Bvr
  * Next Topic: PropertyDispatcher

Preferences Class

A Preferences object controls various model qualities (such as texturing and shading) and hardware preferences. The object contains methods both for setting and retrieving these values. Each preference is identified with a string, and its value is set by either a Boolean, an integer, or a double. Before getting or setting a preference, first obtain the Preference object with the Viewer.getPreferences method.

The first time Microsoft® DirectAnimation™ is run, it establishes the default preferences in the registry. This means the end-user can control what the default preference settings are. When the Viewer.getPreferences method is invoked, the Preferences object contains these settings. These settings can be overridden by calling the set methods on the Preferences object.

A preference called OVERRIDE_APPLICATION_PREFERENCES allows the end-user to decide if a canvas or applet can override the other preferences. If it is TRUE, then explicitly setting the Preferences object will have no effect.

  • Preferences Methods
  • Preferences Fields

Preferences Methods

getBoolean | Returns the value of those preferences that are set by Booleans.
getDouble | Returns the value of those preferences that are set by a double.
getInt | Returns the value of those preferences that are set by an integer.
getString | Returns the value of those preferences that are set by a string.
Preferences | Constructs a Preferences object.
propagate | Tells the system to use the new preferences.
set(preference, Boolean) | Sets the value of those preferences that are determined by booleans.
set(preference, double) | Sets the value of those preferences that are determined by doubles.
set(preference, int) | Sets the value of those preferences that are determined by integers.
set(preference, String) | Sets the value of those preferences that are determined by strings.

getBoolean

Preferences Class

Returns the value of those preferences that are set by Booleans.

Syntax

public Boolean getBoolean( String preference );

Parameters

preference
The name of the preference.

Return Value

Returns a Boolean. For more information about booleans, consult a Java reference.

getDouble

Preferences Class

Returns the value of those preferences that are set by a double.

Syntax

public double getDouble( String preference );

Parameters

preference
The name of the preference.

Return Value

Returns a double. For more information about doubles, consult a Java reference.

getInt

Preferences Class

Returns the value of those preferences that are set by an integer.

Syntax

public int getInt( String preference );

Parameters

preference
The name of the preference.

Return Value

Returns an integer. For more information about integers, consult a Java reference.

getString

Preferences Class

Returns the name of a preference.

Syntax

public String getString( String preference );

Parameters

preference
The name of the preference.

Return Value

Returns the value of those preferences that are set by a string. For more information about strings, consult a Java reference.

Preferences Constructor

Preferences Class

Constructs a Preferences object.

Syntax

public Preferences( );

propagate

Preferences Class

Tells the system to use the new preferences. Use after calling Viewer.getPreferences and the Preference.set() methods but before the model is running.

Syntax

public void propagate( );

set(preference, Boolean)

Preferences Class

Sets the value of those preferences that are determined by Booleans.

Syntax

public void set( String preference, Boolean value );

Parameters

preference
Name of the preference.
value
Value of the preference.

set(preference, double)

Preferences Class

Sets the value of those preferences that are determined by doubles.

Syntax

public void set( String preference, double value );

Parameters

preference
The name of the preference.
value
The value of the preference.

set(preference, int)

Preferences Class

Sets the value of those preferences that are determined by integers.

Syntax

public void set( String preference, int value );

Parameters

preference
Name of the preference.
value
Value of the preference.

set(preference, String)

Preferences Class

Sets the value of those preferences that are determined by strings.

Syntax

public void set( String preference, String value );

Parameters

preference
Name of the preference.
value
Value of the preference.

Preferences Fields

public final static String COLOR_KEY_BLUE
Determines the value of the blue RGB component. The value is set by an integer that can range from 0 to 255. The default value is 245.
public final static String COLOR_KEY_GREEN
Determines the value of the green RGB component. The value is set by an integer that can range from 0 to 255. The default value is 254.
public final static String COLOR_KEY_RED
Determines the value of the red RGB component. The value is set by an integer that can range from 0 to 255. The default value is 1.
public final static String DITHERING
Determines whether dithering is enabled. The value is set by a Boolean. The default mode is Enabled.
public final static String ENGINE_OPTIMIZATIONS
Determines whether or not there will be optimizations. Optimizations include such things as image caching and dirty rectangle optimization. The value is set by a Boolean. The default value is On.
public final static String FILL_MODE
Determines the fill mode, which can be FILL_MODE_POINT, FILL_MODE_SOLID, or FILL_MODE_WIREFRAME. The value is set by an integer. The default mode is Solid.
public final static int FILL_MODE_POINT
Sets the fill mode to point.
public final static int FILL_MODE_SOLID
Sets the fill mode to solid. This is the default mode.
public final static int FILL_MODE_WIREFRAME
Sets the fill mode to wireframe.
public final static String OVERRIDE_APPLICATION_PREFERENCES
A Boolean value that, if true, means the registry overrides any attempts by the application to set preferences. This value cannot be set. It can only be retrieved with getBoolean. The default value is Off.
public final static String PERSPECTIVE_CORRECT
Determines whether or not texturing will be adjusted for perspective. The value is set by a Boolean. The default mode is On.
public final static MAX_FRAMES_PER_SEC
Determines the maximum number of frames per second. The value is set by a double. The default value is 30.
public final static String RGB_LIGHTING_MODE
Determines whether or not the lighting mode is RGB. The value is set by a Boolean. The default value is On.
public final static String SHADE_MODE
Determines what the shading mode will be. The possibilities are SHADE_MODE_FLAT, SHADE_MODE_GOURAUD, or SHADE_MODE_PHONG. The value is set by an integer. The default mode is GOURAUD.
public final static SHADE_MODE_FLAT
Sets the shading mode to flat.
public final static int SHADE_MODE_GOURAUD
Sets the shading mode to Gouraud. This is the default mode.
public final static int SHADE_MODE_PHONG
Sets the shading mode to Phong.
public final static String TEXTURE_QUALITY
Determines what the texture rendering quality will be. The possibilities are TEXTURE_QUALITY_LINEAR or TEXTURE_QUALITY_NEAREST. The value is set by an integer. The default mode is NEAREST.
public final static int TEXTURE_QUALITY_LINEAR
Sets the texture rendering quality to linear.
public final static int TEXTURE_QUALITY_NEAREST
Sets the texture rendering quality to nearest. This is the default mode.
public final static String USE_3-D_HW
Determines whether or not 3-D hardware will be enabled. The value is set by a Boolean. The default is Off. This preference cannot be overridden by the application. It is a read-only value.
public final static USE_VIDEOMEM
Determines whether or not the video memory will be enabled. The value is set by a Boolean. The default is On.


PropertyDispatcher Class-Java Class

PropertyDispatcher Class DirectAnimation Animated Header --PropertyDispatcher Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Preferences
  * Next Topic: SoundBvr

PropertyDispatcher Class

This class allows Microsoft® DirectAnimation™ behaviors to be attached to (and detached from) properties in Microsoft ActiveX® Controls. For example, a NumberBvr can be attached to the width property of a frame on an HTML page.

PropertyDispatcher Methods

detach | Detaches an already attached behavior from an ActiveX control property.
getBvr | Retrieves a behavior that, while it is the same as the behavior used in the constructor, is updated over time.
PropertyDispatcher | Constructs a PropertyDispatcher object.
setControl | Sets the ActiveX control object to which a behavior will be attached.
setPropertyName | Sets the name of the ActiveX control property to which a DirectAnimation behavior will be attached.

detach

PropertyDispatcher Class

Detaches an already attached behavior from an ActiveX control property.

Syntax

public void detach( );

getBvr

PropertyDispatcher Class

Retrieves a behavior that, while it is the same as the behavior used in the constructor, is updated over time. This is the behavior that should be used once the PropertyDispatcher object has been constructed.

Syntax

public Behavior getBvr( );

Return Value

Returns the Behavior object.

PropertyDispatcher

PropertyDispatcher Class

Constructs a PropertyDispatcher object.

Syntax

public PropertyDispatcher( Behavior a );

Parameters

a
Behavior to be attached to an ActiveX control property.

Remarks

The behavior specified by a is used only in the constructor. To attach a behavior to a property, first create the PropertyDispatcher class with that behavior, and then extract an equivalent behavior with getBvr(). Use this behavior in the model. When access to the control and its property name is available, call setControl() and setPropertyName(). Here is an example:

NumberBvr n = the behavior that will be attached to a control
PropertyDispatcher pd = new PropertyDispatcher(n);
NumberBvr attachedN = (NumberBvr)pd.getBvr();

//...At some other point

pd.setControl(ctrl);
pd.setPropertyName("width");

Note that the attachedN behavior must be running to affect the attached property. The behavior is running if:

  • It is part of the image or sound registered with setImage() or setSound() on the model.
  • It was explicitly added to a BvrsToRun list.

setControl

PropertyDispatcher Class

Sets the ActiveX control object to which a behavior will be attached.

Syntax

public void setControl( Object object );

Parameters

object
ActiveX control object to which a behavior will be attached.

setPropertyName

PropertyDispatcher Class

Sets the name of the ActiveX control property to which a DirectAnimation behavior will be attached.

Syntax

public void setPropertyName( String name );

Parameters

name
Name of the property to which a DirectAnimation behavior will be attached.


SoundBvr Class-Java Class

SoundBvr Class DirectAnimation Animated Header --SoundBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: PropertyDispatcher
  * Next Topic: Statics

SoundBvr Class

Creates an object that represents a sound behavior. Sound behaviors have gain, rate, phase, and pan attributes. Imported sounds (.wav and MIDI files) also have a length, returned by the import method.

Gain (volume) defines how loudly the sound is played. Rate defines how many times faster or slower the sound is played relative to its nominal rate. Phase defines how much to shift the time of the sound, and is useful for creating special sound effects such as echoes. Mixing the same sounds that are out of phase makes the sound richer. Pan defines the strength of the sound relative to the left and right sound channels. It is useful for positioning sounds.

Gain, rate, phase, and pan are number behaviors and can, therefore, change over time. This is useful for modifying the sound based, not only on time, (see substituteTime) but also on other computed values such as distance. Sounds can be embedded in a geometry to achieve spatialized effects. Currently, all sounds are omnidirectional.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to SoundBvr Objects
  • Statics Fields Relevant to SoundBvr Objects

SoundBvr Methods

gain | Creates a new sound behavior by multiplying the gain (volume) by the given amount.
loop | Creates a sound behavior that repeats itself continuously.
pan | Creates a sound behavior by repositioning a monophonic sound between the left and right channels.
phase | Creates a sound behavior by shifting the sound in time by the given amount.
rate | Creates a sound behavior by multiplying the playback rate by the given amount.
newUninitBvr | Enables you to refer to an SoundBvr behavior before that behavior has been defined.

gain

SoundBvr Class

Creates a new sound behavior by multiplying the gain (volume) by the given amount.

Syntax

public SoundBvr gain( NumberBvr gainAmt );

Parameters

gainAmt
NumberBvr object specifying the factor by which to multiply the volume. Values greater than one increase the volume and values between zero and 1 decrease the sound's volume. Except for sound geometries, only attenuation values of 0-1 have an effect. For sound geometries, gains greater than 1 affect the distance from which sounds can be heard. The sounds, however, never sound louder than 1. This parameter can also be of type double.

Return Value

Returns the SoundBvr object.

Remarks

This attribute composes multiplicatively.

loop

SoundBvr Class

Creates a sound behavior that repeats itself continuously.

Syntax

public SoundBvr loop( );

Return Value

Returns the SoundBvr object.

Remarks

This method applies to the primitive sounds themselves. It does not loop a reactive sound behavior. Instead, it loops each of the primitive sounds that are the components of the reactive sound behavior. In the following example, snd1 and snd2 are each looped:

SoundBvr snd3 = SoundBvr(until)(snd1, leftButtonDown, snd2);
SoundBvr snd4 = snd3.loop();

This attribute overrides previous values.

pan

SoundBvr Class

Creates a sound behavior by repositioning a monophonic sound between the left and right channels. To do this, the amplitude of the sound in each channel is changed. This is similar to adjusting the balance on a stereo.

Syntax

public SoundBvr pan( NumberBvr panAmt );

Parameters

panAmt
NumberBvr object specifying the amount to pan. Can be a value in the range -1.0 to 1.0, with -1.0 as full left, 0 as center, and 1.0 as full right. This parameter can also be of type double.

Return Value

Returns the SoundBvr object.

Remarks

With stereophonic sounds, panning away from a channel attenuates it.

This attribute composes additively.

phase

SoundBvr Class

Creates a sound behavior by shifting the sound in time by the given amount. Shifting the sound and mixing with the original sound allows for special effects, such as echoing.

Syntax

public SoundBvr phase( NumberBvr phaseAmt );

Parameters

phaseAmt
NumberBvr object specifying the amount in seconds (or fractions of seconds) to shift the sound. This parameter can also be of type double.

Return Value

Returns the SoundBvr object.

Remarks

For unlooped sounds, a positive phase shift delays the sound's starting time while a negative phase shift clips off the beginning of the sound. For looped sounds, a positive phase shift adds the end of the sound to the beginning of the sound, while a negative phase shift controls how far into the sound the beginning should be. You can dynamically change the position of a running sound. This attribute composes additively.

rate

SoundBvr Class

Creates a sound behavior by multiplying the playback rate by the given amount.

Syntax

public SoundBvr rate( NumberBvr rateAmt );

Parameters

rateAmt
NumberBvr object specifying the factor by which to multiply the rate. Values greater than one increase the rate and values between zero and 1 decrease it. This parameter can also be of type double.

Return Value

Returns the SoundBvr object.

Remarks

A rate of 0 pauses the sound. Negative rates are not supported. This attribute composes additively.

newUninitBvr

SoundBvr Class

Enables you to refer to an SoundBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static SoundBvr newUninitBvr( );

Return Value

Returns the SoundBvr object.


Statics Class-Java Class

Statics Class DirectAnimation Animated Header --Statics Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: SoundBvr
  * Next Topic: Behavior Objects

Statics Class

The Statics class collects all the static methods and constants provided by the other classes in com.ms.dxmedia and makes them available as static methods on the Statics class. In addition, the Model class extends from Statics. This means that, when a programmer is implementing the createModel method of the class they extend from Model, all the static methods in Statics become available without qualification.

The following lists show the classes that have relevant Statics methods or fields.

Statics Methods Relevant to Other Classes

  • Statics Methods Relevant to Behavior Objects
  • Statics Methods Relevant to BooleanBvr Objects
  • Statics Methods Relevant to CameraBvr Objects
  • Statics Methods Relevant to ColorBvr Objects
  • Statics Methods Relevant to DXMApplet Objects
  • Statics Methods Relevant to DXMEvent Objects
  • Statics Methods Relevant to FontStyleBvr Objects
  • Statics Methods Relevant to GeometryBvr Objects
  • Statics Methods Relevant to ImageBvr Objects
  • Statics Methods Relevant to MatteBvr Objects
  • Statics Methods Relevant to MontageBvr Objects
  • Statics Methods Relevant to NumberBvr Objects
  • Statics Methods Relevant to Path2Bvr Objects
  • Statics Methods Relevant to Point2Bvr Objects
  • Statics Methods Relevant to Point3Bvr Objects
  • Statics Methods Relevant to SoundBvr Objects
  • Statics Methods Relevant to StringBvr Objects
  • Statics Methods Relevant to Transform2Bvr Objects
  • Statics Methods Relevant to Transform3Bvr Objects
  • Statics Methods Relevant to TupleBvr Objects
  • Statics Methods Relevant to Vector2Bvr Objects
  • Statics Methods Relevant to Vector3Bvr Objects

Statics Fields Relevant to Other Classes

  • Statics Fields Relevant to BooleanBvr Objects
  • Statics Fields Relevant to ColorBvr Objects
  • Statics Fields Relevant to DashStyleBvr Objects
  • Statics Fields Relevant to DXMEvent Objects
  • Statics Fields Relevant to EndStyleBvr Objects
  • Statics Fields Relevant to FontStyleBvr Objects
  • Statics Fields Relevant to GeometryBvr Objects
  • Statics Fields Relevant to ImageBvr Objects
  • Statics Fields Relevant to JoinStyleBvr Objects
  • Statics Fields Relevant to LineStyleBvr Objects
  • Statics Fields Relevant to MatteBvr Objects
  • Statics Fields Relevant to MicrophoneBvr Objects
  • Statics Fields Relevant to MontageBvr Objects
  • Statics Fields Relevant to NumberBvr Objects
  • Statics Fields Relevant to Point2Bvr Objects
  • Statics Fields Relevant to Point3Bvr Objects
  • Statics Fields Relevant to SoundBvr Objects
  • Statics Fields Relevant to Transform2Bvr Objects
  • Statics Fields Relevant to Transform3Bvr Objects
  • Statics Fields Relevant to Vector2Bvr Objects
  • Statics Fields Relevant to Vector3Bvr Objects

Miscellaneous Statics Methods and Fields

  • Statics Methods (Miscellaneous)
  • Statics Fields (Miscellaneous)


Statics Methods Relevant to Behavior Objects-Java

Statics Methods Relevant to Behavior Objects DirectAnimation Animated Header --Statics Methods Relevant to Behavior Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Statics
  * Next Topic: BooleanBvr Objects

Statics Methods Relevant to Behavior Objects

cond | Creates a new behavior from the BooleanBvr and two other behaviors.
sequence | Creates a new behavior that is the sum of the two Behavior parameters.
until | Creates a Behavior that changes when a given event occurs.
untilEx | Creates a Behavior that changes upon a given event.
untilNotify | Creates a Behavior that changes upon a given event to the behavior returned by the event notifier notifier.

cond

Statics Class

Creates a new behavior from the BooleanBvr and two other behaviors. The value of the new behavior, at any point in time, is one of those two behaviors. Which of the behaviors is chosen depends on the value of the BooleanBvr.

Syntax

public static Behavior cond( BooleanBvr bool, Behavior a, Behavior b );

Parameters

bool
BooleanBvr object that determines which of the two behaviors will become the value of the new behavior.
a
First of the two possible values the new behavior can assume.
b
Second of the two possible values the new behavior can assume.

Return Value

Returns the Behavior object.

Example

// Create a new behavior, y, which is either 1.0 if x > 1.0,
// or another behavior, x
NumberBvr y = (NumberBvr)cond(gte(x, toBvr(1.0)), toBvr(1.0), x);

sequence

Statics Class

Creates a new behavior that is the sum of the two Behavior parameters. In other words, the two behaviors are concatenated, according to their order in the parameter list. Once the duration is over, the behavior becomes a snapshot of the second behavior. An exception is when the first behavior is of infinite duration. In this case, the returned behavior is always the infinite behavior, no matter what the second behavior is.

Syntax

public static Behavior sequence( Behavior a, Behavior b );

Parameters

a
First behavior in the sequence.
b
Second behavior in the sequence.

Return Value

Returns the Behavior object.

Remarks

Notice that, in contrast to compose, which first applies the right-hand argument and then the left, sequence first applies the left-hand argument and then the right.

until

Statics Class

Creates a behavior that changes when a given event occurs. Up to and including the time the event occurs, the behavior is the same as a. After the event e occurs, the behavior changes to be the same as b. Because until takes behaviors and returns a behavior, it can be nested.

Syntax

public static Behavior until( Behavior a, DXMEvent e, Behavior b );

Parameters

a
Behavior object that the behavior represents initially.
e
DXMEvent object specifying the event.
b
Behavior object that the behavior represents after the event.

Return Value

Returns the Behavior object.

Example

//The ColorBvr col is initially red
//It turns blue when the left mouse button is pressed
ColorBvr c = (ColorBvr)until(red, leftButtonDown, blue);

//Example of nested behaviors
//The behavior is initially red
//It turns blue when the left mouse button is pressed
//And turns green when it is pressed again
ColorBvr c = (ColorBvr)until(red, leftButtonDown,
            until(blue, leftButtonDown, green));

untilEx

Statics Class

Creates a Behavior that changes upon a given event. Initially, the behavior is the same as a. When event e occurs, the behavior changes to the behavior returned by the event. This means that only DXMEvents that return a Behavior can be used. These events can be constructed with attachData, notifyEvent, and snapshotEvent.

Syntax

public static Behavior untilEx( Behavior a, DXMEvent e );

Parameters

a
Behavior object that the behavior represents initially.
e
DXMEvent object that triggers the switch and returns the new behavior.

Return Value

Returns the Behavior object caused by the event e.

untilNotify

Statics Class

Creates a behavior that changes upon a given event. Initially, the behavior is the same as a. When event e occurs, the behavior changes to the behavior returned by the event notifier notifier.

Syntax

public static Behavior untilNotify( Behavior a, DXMEvent e,
UntilNotifier notifier );

Parameters

a
Behavior object that the behavior represents initially.
e
DXMEvent object specifying the event.
notifier
UntilNotifier object. The notify method of this object, called on the given event, returns the new behavior.

Return Value

Returns the Behavior object.


Statics Methods Relevant to BooleanBvr Objects-Java

Statics Methods Relevant to BooleanBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to BooleanBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Behavior Objects
  * Next Topic: CameraBvr Objects

Statics Methods Relevant to BooleanBvr Objects

and | Creates a Boolean behavior that represents the logical AND of the given behaviors.
keyState | Creates a Boolean behavior that describes whether the key is up or down.
not | Creates a Boolean behavior that represents the logical NOT of the given behavior.
or | Creates a Boolean behavior that represents the logical OR of the given behaviors.
toBvr | Converts a Boolean to a BooleanBvr.

and

Statics Class

Creates a Boolean behavior that represents the logical AND of the given behaviors. The behavior's value is true when both b1 and b2 are true; otherwise, it is false.

Syntax

public static BooleanBvr and( BooleanBvr b1, BooleanBvr b2 );

Parameters

b1 and b2
BooleanBvr objects to combine.

Return Value

Returns the BooleanBvr object.

See Also

or, not

keyState

Statics Class

Creates a Boolean behavior that describes whether the key is up or down.

Syntax

public static BooleanBvr keyState( int keyCode );

Parameters

keyCode
Can either be quoted characters, such as 'a' or specific Java keycode constants.

Return Value

Returns the BooleanBvr object. The value is TRUE when the key is down and FALSE when the key is up.

not

Statics Class

Creates a Boolean behavior that represents the logical NOT of the given behavior. The behavior's value is true when b is false; otherwise, it is false.

Syntax

public static BooleanBvr not( BooleanBvr b );

Parameters

b
BooleanBvr object to apply the operation to.

Return Value

Returns the BooleanBvr object.

See Also

and , or

or

Statics Class

Creates a Boolean behavior that represents the logical OR of the given behaviors. The behavior's value is true when either b1 or b2 are true; otherwise it is false.

Syntax

public static BooleanBvr or( BooleanBvr b1, BooleanBvr b2 );

Parameters

b1 and b2
BooleanBvr objects to combine.

Return Value

Returns the BooleanBvr object.

See Also

and, not

toBvr

Statics Class

Converts a Boolean to a BooleanBvr. The only time toBvr is used is when converting Java numbers, Booleans, or strings to their corresponding Microsoft® DirectAnimation™ types. It is not used for constants such as red, yVector3, and origin2 because these are defined as constant behaviors.

Syntax

public static BooleanBvr toBvr( boolean bool );

Parameters

bool
Java Boolean type to be converted.

Return Value

Returns the BooleanBvr object.


Statics Methods Relevant to CameraBvr Objects-Java

Statics Methods Relevant to CameraBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to CameraBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: BooleanBvr Objects
  * Next Topic: ColorBvr Objects

Statics Methods Relevant to CameraBvr Objects

parallelCamera | Creates a parallel camera for parallel (or orthographic) projection.
perspectiveCamera | Creates a perspective camera for perspective rendering.

parallelCamera

Statics Class

Creates a parallel camera for parallel (or orthographic) projection. The parallel camera is transformed the same way as the perspective camera, except that z-scales have no effect (note, however, that zero or negative z-scales yield undefined results).

Prior to transformation, the parallel camera gazes in the negative z-direction, with positive y considered as "up."

Syntax

public static CameraBvr parallelCamera( NumberBvr nearclip );

Parameters

nearclip
NumberBvr object that is the z-location of the near clipping plane. This parameter can also be of type double.

Return Value

Returns the CameraBvr object.

perspectiveCamera

Statics Class

Creates a perspective camera for perspective rendering. On creation, the projection plane (the plane at which objects appear as their actual size) is located at Z=0, the projection point is located at [0 0 projdist], and the near clipping plane is located at [0 0 nearclip].

Syntax

public static CameraBvr perspectiveCamera(
NumberBvr projdst, NumberBvr nearclip );

Parameters

projdst
NumberBvr object that is the z-location of the projection point. It must be greater than zero. This parameter can also be of type double.
nearclip
NumberBvr object that is the z-location of the near clipping plane. It must be less than projdst. This parameter can also be of type double.

Return Value

Returns the CameraBvr object.

Remarks

The projdst parameter specifies the distance between the projection plane and the projection point, and must be greater than zero. The smaller this distance, the more of a wide-angle effect is achieved. The larger this distance, the more of a telephoto effect is achieved.

The nearclip parameter specifies the z-location of the near clipping plane and must be less than projdst. In other words, the near clipping plane must lie on the same side of the projection point as the projection plane. Although it is tempting to place the near clipping plane as close as possible to the projection point, z-buffer precision is lost dramatically as the near clipping plane gets closer to the projection point. (The main result of such precision loss is that far objects are rendered in front of near objects.)

The following diagram shows a model of the perspective camera. Note that the nearclip need not be in front of the projection plane.


Statics Methods Relevant to ColorBvr Objects-Java

Statics Methods Relevant to ColorBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to ColorBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: CameraBvr Objects
  * Next Topic: DXMApplet Objects

Statics Methods Relevant to ColorBvr Objects

colorHsl | Creates a color behavior that represents a color given by hue, saturation, and lightness values.
colorRgb | Creates a color behavior that represents a color given by red, green, and blue intensity values.
colorRgb255 | Creates a color behavior that represents a color given by red, green, and blue intensity values. These values can be in the range [0, 255].

colorHsl

Statics Class

Creates a color behavior that represents a color given by hue, saturation, and lightness values. The behavior's value at any given time depends on the values of h, s, and l.

Syntax

public static ColorBvr colorHsl(
NumberBvr h, NumberBvr s, NumberBvr l );

Parameters

h
NumberBvr object specifying the hue or base color. 0.0 is red, 0.33 is green, and 0.67 is blue. This number is considered "modulo 1," which means, for example, that the numbers 1.67, and 0.67 are both considered to be 0.67. This parameter can also be of type double.
s
NumberBvr object specifying saturation or intensity of the hue. If saturation is 0.0, the color produced is gray regardless of the hue value. This parameter can also be of type double.
l
NumberBvr object specifying lightness or amount of white in the color. If lightness is 1.0, the color produced is white regardless of the hue and saturation values. Similarly, if lightness is 0.0, the color is black. This parameter can also be of type double.

Return Value

Returns the ColorBvr object.

Remarks

Hue, saturation, and lightness values can range from 0.0 to 1.0. If the corresponding number behavior has a value outside this range, then the number is considered modulo 1.

Example

The following example creates a color whose hue varies with time, but whose saturation and lightness values are constant:

//vary the hue over time
ColorBvr animCol = colorHsl(localTime, toBvr(0.5), toBvr(0.5));

See Also

colorRgb

colorRgb

Statics Class

Creates a color behavior that represents a color given by red, green, and blue intensity values. The behavior's value at any given time depends on the values of r, g, and b.

Syntax

public static ColorBvr colorRgb(
NumberBvr r, NumberBvr g, NumberBvr b );

Parameters

r, g, and b
NumberBvr objects specifying the red, green, and blue values, respectively. The value of each specifies that color's intensity, with 0.0 for no color and 1.0 for the highest possible intensity. These parameters can also be of type double.

Return Value

Returns the ColorBvr object.

Remarks

Intensity values can range from 0.0 to 1.0. If the corresponding number behavior has a value outside this range, the integer part of the value is discarded and only the fractional part is used.

Example

The following example creates a color behavior whose red component varies from 0 to 1:

//Create a sawtooth wave that goes repetitively from 0 to 1
//Do this by taking the modulus of localTime and 1
NumberBvr sawtooth = mod(localTime, toBvr(1));

//Now create the RGB color, making red = to the value
//of sawtooth
ColorBvr col = colorRgb(sawtooth, toBvr(0), toBvr(0));

See Also

colorHsl

colorRgb255

Statics Class

Creates a color behavior that represents a color given by red, green, and blue intensity values. The behavior's value at any given time depends on the values of r, g, and b. These values can be in the range [0, 255].

Syntax

public static ColorBvr colorRgb255( short r, short g, short b );

Parameters

r, g, and b
Shorts specifying the red, green, and blue values, respectively. The value of each specifies that color's intensity, with 0 for no color and 255 for the highest possible intensity.

Return Value

Returns the ColorBvr object.


Statics Methods Relevant to DXMApplet Objects-Java

Statics Methods Relevant to DXMApplet Objects DirectAnimation Animated Header --Statics Methods Relevant to DXMApplet Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ColorBvr Objects
  * Next Topic: DXMEvent Objects

Statics Methods Relevant to DXMApplet Objects

registerErrorAndWarningReceiver See registerErrorAndWarningReceiver in the Viewer Interface section.

registerErrorAndWarningReceiver

Statics Class

Enables the application to register an object that will have its methods invoked upon asynchronous errors encountered when Microsoft® DirectAnimation™ media is controlling the frame loop or when recoverable errors occur when sampling and displaying the model.

Syntax

public static ErrorAndWarningReceiver
     registerErrorAndWarningReceiver(
     ErrorAndWarningReceiver w
);

Parameters

w
ErrorAndWarningReceiver object to be registered.

Return Value

Returns the ErrorAndWarningReceiver object.


Statics Methods Relevant to DXMEvent Objects-Java

Statics Methods Relevant to DXMEvent Objects DirectAnimation Animated Header --Statics Methods Relevant to DXMEvent Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DXMApplet Objects
  * Next Topic: FontStyleBvr Objects

Statics Methods Relevant to DXMEvent Objects

andEvent | Creates an event that occurs only when the two given events happen simultaneously.
keyDown | Retrieves a DXMEvent when a specific key is pressed.
keyUp | Retrieves a DXMEvent when a specific key is released.
notEvent | Creates an event that occurs when the given event does not occur.
orEvent | Creates an event that occurs when either one of the given events occurs.
predicate | Creates an event that occurs when the value of the given Boolean behavior changes from false to true.
thenEvent | Creates an event if e2 occurs after e1.
timer | Creates an event that occurs when the specified time has elapsed.
unregisterCallback | Used to unregister the callback.

andEvent

Statics Class

Creates an event that occurs only when the two given events happen simultaneously. The event time for the new event is when both of the given events occur.

Syntax

public static DXMEvent andEvent( DXMEvent first, DXMEvent second );

Parameters

first and second
DXMEvent objects.

Return Value

Returns a DXMEvent object. When the event occurs, it produces a PairObject. The first member is the event data returned by the first event. The second member is the event data returned by the second event.

keyDown

Statics Class

Retrieves a DXMEvent when a specific key is pressed. This is a state-transition event.

Syntax

public static DXMEvent keyDown( int keyCode );

Parameters

keyCode
Can either be quoted characters such as 'a' or specific Java keycode constants.

Return Value

Returns the DXMEvent object. This event produces no data.

Remarks

The following table lists the hexadecimal values for all the special keycodes. The pattern for each key is 0x10000 plus the Win32 scan code.
Key | KeyCode
PGUP | 10021
PGDN | 10022
END | 10023
HOME | 10024
LEFT | 10025
UP | 10026
RIGHT | 10027
DOWN | 10028
INSERT | 1002D
DELETE | 1002E
NUMPAD0 | 10060
NUMPAD1 | 10061
NUMPAD2 | 10062
NUMPAD3 | 10063
NUMPAD4 | 10064
NUMPAD5 | 10065
NUMPAD6 | 10066
NUMPAD7 | 10067
NUMPAD8 | 10068
NUMPAD9 | 10069
MULTIPLY | 1006A
ADD | 1006B
SEPARATOR | 1006C
SUBTRACT | 1006D
DECIMAL | 1006E
DIVIDE | 1006F
F1 | 10070
F2 | 10071
F3 | 10072
F4 | 10073
F5 | 10074
F6 | 10075
F7 | 10076
F8 | 10077
F9 | 10078
F10 | 10079
F11 | 1007A
F12 | 1007B
F13 | 1007C
F14 | 1007D
F15 | 1007E
F16 | 1007F
F17 | 10080
F18 | 10081
F19 | 10082
F20 | 10083
F21 | 10084
F22 | 10085
F23 | 10086
F24 | 10087

keyUp

Statics Class

Retrieves a DXMEvent when a specific key is released. This is a state-transition event.

Syntax

public static DXMEvent keyUp( int keyCode );

Parameters

keyCode
Can either be quoted characters such as 'a' or specific Java keycode constants.

Return Value

Returns the DXMEvent object. This event produces no data.

Remarks

For a list of special keycodes, see keyDown.

notEvent

Statics Class

Creates an event that occurs when the given event does not occur. This is useful for creating combined events that occur only if the given event is not also occurring, for example, an event that occurs when the left mouse button is down and the right button is not down.

Syntax

public static DXMEvent notEvent( DXMEvent ev );

Parameters

ev
DXMEvent object.

Return Value

Returns the DXMEvent object. This event produces no data.

orEvent

Statics Class

Creates an event that occurs when either one of the given events occurs. The event time for the new event is when either the first or second given event has occurred.

Syntax

public static DXMEvent orEvent( DXMEvent first, DXMEvent second );

Parameters

first and second
DXMEvent objects.

Return Value

Returns a DXMEvent object. Its data is the event data of either the first or second event.

predicate

Statics Class

Creates an event that occurs when the value of the given Boolean behavior changes from false to true.

Syntax

public static DXMEvent predicate( BooleanBvr bool );

Parameters

bool
BooleanBvr object.

Return Value

Returns the DXMEvent. This event produces no data.

Remarks

Because of implementation dependencies and the potential for aliasing, the system cannot always detect when a Boolean behavior becomes true. For example, the expression: predicate(eq(sin(localTime), toBvr(0))) may never occur unless the system happens to sample it when sin(localTime) is exactly zero. Therefore, it is advisable to use inequality comparisons (for example, gte) rather than equality comparisons (for example, eq). Alternatively, the timer constructor may be a better choice for these situations.

thenEvent

Statics Class

Creates an event if e2 occurs after e1. The event time of the new event is the time of the second event.

Syntax

public static DXMEvent thenEvent( DXMEvent e1, DXMEvent e2 );

Parameters

e1
First event, that, when it occurs, triggers the second event.
e2
Second event, caused by the occurrence of the first event.

Return Value

Returns the DXMEvent object. Its event data is the time the second event fired and a PairObject containing the event data for both events.

timer

Statics Class

Creates an event that occurs when the specified time has elapsed.

Syntax

public static DXMEvent timer( NumberBvr timeout );

Parameters

timeout
NumberBvr object. This parameter can also be of type double.

Return Value

Returns the DXMEvent object. This event produces no data.

unregisterCallback

Statics Class

Used to unregister the callback.

Syntax

public static void unregisterCallback( Object object );

Parameters

object
Object used to unregister the callback.

See Also

registerCallback


Statics Methods Relevant to FontStyleBvr Objects-Java

Statics Methods Relevant to FontStyleBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to FontStyleBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DXMEvent Objects
  * Next Topic: GeometryBvr Objects

Statics Methods Relevant to FontStyleBvr Objects

font Specifies the face, size in points, and color of a FontStyleBvr object.

font

Statics Class

Specifies the face, size in points, and color of a FontStyleBvr object. Because these parameters are all behaviors, they can vary over time.

Syntax

public static FontStyleBvr font(
StringBvr face, NumberBvr points, ColorBvr color>;

Parameters

face
StringBvr object specifying the font face. This parameter can also be of type java.lang.String.
points
NumberBvr object specifying the font size, in points. This parameter can also be of type double.
color
ColorBvr object specifying the font color.

Return Value

Returns the FontStyleBvr object.


Statics Methods Relevant to GeometryBvr Objects-Java

Statics Methods Relevant to GeometryBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to GeometryBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: FontStyleBvr Objects
  * Next Topic: ImageBvr Objects

Statics Methods Relevant to GeometryBvr Objects

importGeometry(url) | Creates a geometry behavior by synchronously importing the given VRML or .x file.
importGeometry(url, geoStandIn, ev, progress, size) | Creates a geometry behavior by asynchronously importing the given VRML or .x file.
soundSource | Creates a geometry behavior that contains the given sound behavior.
spotLight | Creates a spotlight geometry.
union | Creates a geometry behavior by combining the given geometries.
unionArray | Creates a geometry behavior by combining the geometries in the array.

importGeometry(url)

Statics Class

Creates a geometry behavior by synchronously importing the given VRML or .x file. Synchronous loads are blocking calls, which means that ticking will not continue on any model until all synchronous downloads are completed. These downloads should be small and listed before asynchronous downloads. Use asynchronous downloads for more complex images and sounds. See below for a description of the asynchronous import method.

Syntax

public static GeometryBvr importGeometry( URL url );

Parameters

url
Fully qualified path of the file to be downloaded. This parameter is of type java.net.URL.

Return Value

Returns the GeometryBvr object.

importGeometry(url, geoStandIn, ev, progress, size)

Statics Class

Creates a geometry behavior by asynchronously importing the given VRML or .x file. Asynchronous downloads should be listed after synchronous ones and used for more complex sounds and images. For a description of the synchronous import method, see above.

Syntax

public static GeometryBvr importGeometry(
     URL url,
     GeometryBvr geoStandIn,
     DXMEvent[] ev,
     NumberBvr[] progress,
     NumberBvr[] size
);

Parameters

url
Fully qualified path of the geometry to be downloaded. This parameter is of type java.net.URL.
geoStandIn
Geometry to use while the contents at url are still being downloaded. This argument cannot be null.
ev
Event to trigger once the download is complete. This argument can be null.
progress
NumberBvr that goes from 0 to 1, tracking the progress of the download. For files downloaded via http, progress is reported as the percentage of the file that has been downloaded. For other protocols, the value may simply start at 0 and change to 1 once the download is complete. This argument can be null.
size
Size of the geometry, in bytes. The value begins at -1 and will change to the true size if it is available. This argument can be null.

Return Value

Returns the GeometryBvr object.

soundSource

Statics Class

Creates a geometry behavior that contains the given sound behavior. The bounding box is the empty bounding box, where the maximum coordinate is less than the minimum coordinate.

Syntax

public static GeometryBvr soundSource( SoundBvr sound );

Parameters

sound
SoundBvr object.

Return Value

Returns the GeometryBvr object.

spotLight

Statics Class

Creates a spotlight geometry. A spotlight is a positioned, directed light source initially located at the origin and directed along the negative z-axis.

Syntax

public static GeometryBvr spotLight(
NumberBvr fullcone, NumberBvr cutoff );

Parameters

fullcone
NumberBvr object specifying the angle in radians from the main spotlight axis for which surfaces will receive full illumination from the light.
cutoff
NumberBvr object specifying the angle in radians from the main spotlight axis past which surfaces will receive no illumination from the light. This parameter can also be of type double.

Return Value

Returns the GeometryBvr object.

Remarks

Examples of real-world spotlights include desk lamps, headlights, and flashlights. The bounding box is the empty bounding box, where the maximum extent is less than the minimum extent.

The fullcone parameter specifies the half-angle of the region that will receive the full intensity of the spotlight. This parameter can have values from 0.00 to pi.

The cutoff parameter specifies the half-angle of the region past which surfaces will receive no illumination. This parameter can have values from the value of fullcone to pi.

Surfaces receive illumination from the spotlight in the following manner:

  • If the surface point is within fullcone radians of the main spotlight axis, the spotlight acts as a point light source (including distance attenuation effects).
  • If the surface point is greater than fullcone radians, but is within cutoff radians of the main spotlight axis, the surface receives illumination that is attenuated by the angular displacement.
  • If the surface point is greater than cutoff radians from the main spotlight axis, it receives no illumination.

These rules are illustrated in the following diagram.

The following diagram shows how the intensity of the spotlight changes in relationship to the angle:

union

Statics Class

Creates a geometry behavior by combining the given geometries. The new geometry contains all elements of the original geometries. The bounding box of a set of unioned geometries is simply the union of the bounding boxes of each of the geometries.

Syntax

public static GeometryBvr union( GeometryBvr g1, GeometryBvr g2 );

Parameters

g1 and g2
GeometryBvr objects.

Return Value

Returns the GeometryBvr object.

unionArray

Statics Class

Creates a geometry behavior by combining the geometries in the array. The new geometry contains all elements of the original geometries. The bounding box of a set of unioned geometries is simply the union of the bounding boxes of each of the geometries.

Syntax

public static GeometryBvr unionArray( GeometryBvr[], geometries );

Parameters

geometries
Array of GeometryBvr objects.

Return Value

Returns the GeometryBvr object.


Statics Methods Relevant to ImageBvr Objects-Java

Statics Methods Relevant to ImageBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to ImageBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: GeometryBvr Objects
  * Next Topic: MatteBvr Objects

Statics Methods Relevant to ImageBvr Objects

gradientHorizontal | Creates an image behavior that smoothly interpolates between the start and stop colors.
gradientPolygon | Creates a polygon from an array of points and an array of colors.
gradientSquare | Creates an image behavior with a square color wash. The image is a unit-sized square, centered at the origin, reaching from [-0.5, -0.5] to [0.5, 0.5].
hatchBackwardDiagonal | Creates backward diagonal (lines that slant from right to left) hatch marks.
hatchCross | Creates cross-hatched markings.
hatchDiagonalCross | Creates diagonally criss-crossed hatch marks.
hatchForwardDiagonal | Creates forward diagonal (lines that slant from left to right) hatch marks.
hatchHorizontal | Creates horizontal hatch lines.
hatchVertical | Creates vertical hatch lines.
importImage(url) | Creates an image behavior by synchronously importing the given bitmap.
importImage(url, imgStandIn, ev, progress, size) | Creates an image behavior by asynchronously importing the given bitmap.
importImageColorKey(url, red, green, blue) | Allows a particular color in the synchronously imported image, specified by an RGB value, to be treated as transparent.
importImageColorKey(url, imgStandIn, ev, progress, size, red, green, blue) | Allows a particular color in the asynchronously imported image, specified by an RGB value, to be treated as transparent.
importMovie(url, img, snd) | Downloads a movie synchronously.
importMovie(url, img, snd, imgStandIn, sndStandIn, ev, progress, size) | Imports a movie asynchronously.
overlay | Creates an image behavior by laying the image behavior i1 over the image behavior i2.
overlayArray | Creates an image behavior by layering the ImageBvr objects.
radialGradientPolygon | Creates an image behavior that is a regular polygon with a radial fill.
radialGradientRegularPoly | Creates an image behavior that is a regular, gradient polygon with the specified number of sides (there must be at least 3) and a radial fill.
radialGradientSquare | Creates an image behavior with a square radial fill. The image is a unit-sized square, centered at the origin, reaching from [-0.5, -0.5] to [0.5, 0.5].
solidColorImage | Creates an image behavior and applies the given color behavior to all points in the image.
stringImage | Constructs an ImageBvr object from a StringBvr object and a FontStyleBvr object.

gradientHorizontal

Statics Class

Creates an image behavior that smoothly interpolates between the start and stop colors. Colors start on the left and go toward the right, based on the power specified by fallOff. The extent of the generated image is [-0.5,-0.5] to [0.5,0.5].

Syntax

public static ImageBvr gradientHorizontal(
ColorBvr start, ColorBvr stop, NumberBvr fallOff );

Parameters

start
ColorBvr object representing the first color of the gradient fill.
stop
ColorBvr object representing the final color of the gradient fill.
fallOff
NumberBvr object used to control how rapidly one color transitions into another. A value of 1 is a linear transition. For 0 < power < 1 the transition occurs rapidly, while power > 1 means that the transition occurs more slowly. For example, if you are transitioning from red to blue, and power = 2, you will see a great deal of red in the gradient fill. On the other hand, if power = 0.5 (the inverse of 2), you will see very little red. If power = 0, you will see only blue. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

gradientPolygon

Statics Class

Creates a polygon from an array of points and an array of colors. The polygon has a smooth interpolation between the colors at the vertices. This interpolation is in RGB space. The image is transparent and unpickable outside the polygon. The polygon must be convex and fanned, with at least 3 vertices. The tessellation into triangles occurs by fanning around the first vertex. If, for example, the first triangle is from vertices [0, 1, 2], the second is from [0, 2, 3], and so on. The tessellation determines how the color interpolation is done. The bounding box is the box surrounding the points of the polygon.

Syntax

public static ImageBvr gradientPolygon(
Point2Bvr[] pts, ColorBvr[] colors );

Parameters

pts
Array of points that determine the polygon's vertices.
colors
Array of colors corresponding to the vertices.

Return Value

Returns the ImageBvr object.

gradientSquare

Statics Class

Creates an image behavior with a square color wash. The image is a unit-sized square, centered at the origin, reaching from [-0.5, -0.5] to [0.5, 0.5]. The image contains the specified colors in the specified corners. The gradient fills are determined by bilinearly interpolating between these specified colors. (This means that, rather than being simply a four-cornered polygon, it is a polygon with four corners plus a point in the middle that is the average of the four colors.) The image is transparent and unpickable outside of this region. The interpolation is in RGB space. The bounding box is a box from (-0.5, -0.5) to (0.5, 0.5).

Syntax

public static ImageBvr gradientSquare(
     ColorBvr lowerLeft,
     ColorBvr upperLeft,
     ColorBvr upperRight,
     ColorBvr lowerRight
);

Parameters

lowerLeft
Color in the lower-left corner of square.
upperLeft
Color in the upper-left corner of the square.
upperRight
Color in the upper-right corner of the square.
lowerRight
Color in the lower-right corner of the square.

Return Value

Returns the ImageBvr object.

hatchBackwardDiagonal

Statics Class

Creates backward diagonal (lines that slant from right to left) hatch marks. All hatches fit in a box, of dimension size, that is infinitely tiled. For perpendicular hatches, the size of the box is twice the distance between the hatch lines. By default, hatch marks are black and appear on a transparent background.

Syntax

public static ImageBvr hatchBackwardDiagonal(
ColorBvr color NumberBvr size );

Parameters

color
ColorBvr object representing the line color.
size
NumberBvr object representing the size of the hatch box. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

hatchCross

Statics Class

Creates cross-hatched markings. This method can be thought of as a combination of the hatchVertical and the hatchHorizontal methods. All hatches fit in a box, of dimension size, that is infinitely tiled. For perpendicular hatches, the size of the box is twice the distance between the hatch lines. By default, hatch marks are black and appear on a transparent background.

Syntax

public static ImageBvr hatchCross( ColorBvr color, NumberBvr size );

Parameters

color
ColorBvr object representing the line color.
size
NumberBvr object representing the size of the hatch box. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

hatchDiagonalCross

Statics Class

Creates diagonally criss-crossed hatch marks. All hatches fit in a box, of dimension size, that is infinitely tiled. For perpendicular hatches, the size of the box is twice the distance between the hatch lines. By default, hatch marks are black and appear on a transparent background.

Syntax

public static ImageBvr hatchDiagonalCross(
ColorBvr color, NumberBvr size );

Parameters

color
ColorBvr object representing the line color.
size
NumberBvr object representing the size of the hatch box. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

hatchForwardDiagonal

Statics Class

Creates forward diagonal (lines that slant from left to right) hatch marks. All hatches fit in a box, of dimension size, that is infinitely tiled. For perpendicular hatches, the size of the box is twice the distance between the hatch lines.

Syntax

public static ImageBvr hatchForwardDiagonal(
ColorBvr color, NumberBvr size );

Parameters

color
ColorBvr object representing the line color.
size
NumberBvr object representing the size of the hatch box. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

hatchHorizontal

Statics Class

Creates horizontal hatch lines. All hatches fit in a box, of dimension size, that is infinitely tiled. For perpendicular hatches, the size of the box is twice the distance between the hatch lines. By default, hatch marks are black and appear on a transparent background.

Syntax

public static ImageBvr hatchHorizontal(
ColorBvr color, NumberBvr size );

Parameters

color
ColorBvr object representing the line color.
size
NumberBvr object representing the size of the hatch box. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

hatchVertical

Statics Class

Creates vertical hatch lines. All hatches fit in a box, of dimension size, that is infinitely tiled. For perpendicular hatches, the size of the box is twice the distance between the hatch lines. By default, hatch marks are black and appear on a transparent background.

Syntax

public static ImageBvr hatchVertical( ColorBvr color, NumberBvr size );

Parameters

color
ColorBvr object representing the line color.
size
NumberBvr object representing the size of the hatch box. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

importImage(url)

Statics Class

Creates an image behavior by synchronously importing the given bitmap.

Syntax

public static importImage( URL url );

Parameters

url
Fully qualified path of the image to be downloaded. This parameter is of type java.net.URL.

Return Value

Returns the ImageBvr object.

Remarks

The pixels of the bitmap are mapped to points in the image such that the center of the bitmap is placed at (0,0). The extent of the points affected depends on the width and height of the bitmap in pixels and the resolution of the bitmap in pixels per meter. The bounding box of an imported image is calculated as follows: If the pixel dimensions are W´H, and given that:

halfWidthMeters = Statics.pixel * W/2;
halfHeightMeters = Statics.pixel * H/2;

then the bounding box will be (-halfWidthMeters, -halfHeightMeters) to (halfWidthMeters, halfHeightMeters) with the resultant image centered at the origin.

Synchronous loads are blocking calls, which means that ticking will not continue on any model until all synchronous downloads are completed. These downloads should be small and listed before asynchronous downloads. Use asynchronous downloads for more complex images and sounds. See below for a description of the asynchronous import method.

The file formats that can be imported are .PNG, .JPG, .BMP, and .GIF.

See Also

importImageColorKey

importImage(url, imgStandIn, ev, progress, size)

Statics Class

Creates an image behavior by asynchronously importing the given bitmap.

Syntax

public static ImageBvr importImage(
     URL url,
     ImageBvr imgStandIn,
     DXMEvent[] ev,
     NumberBvr[] progress,
     NumberBvr[] size
);

Parameters

url
Fully qualified path of the image to be downloaded. This parameter is of type java.net.URL.
imgStandIn
Image to use while the contents at url are still being downloaded. This argument cannot be null, however, the image can be empty.
ev
Event to trigger once the download is complete. This argument can be null.
progress
NumberBvr that goes from 0 to 1, tracking the progress of the download. For files downloaded via http, progress is reported as the percentage of the file that has been downloaded. Other protocols, the value may simply start at 0 and change to 1 once the download is complete. This argument can be null.
size
NumberBvr that is the size of the image, in bytes. The value begins at -1 and will change to the true size if it is available. This argument can be null.

Return Value

Returns the ImageBvr object.

Remarks

The pixels of the bitmap are mapped to points in the image such that the center of the bitmap is placed at (0,0). The extent of the points affected depends on the width and height of the bitmap in pixels and the resolution of the bitmap in pixels per meter. The bounding box of an imported image is calculated as follows: If the pixel dimensions are W´H, and given that:

halfWidthMeters = Statics.pixel * W/2;
halfHeightMeters = Statics.pixel * H/2;

then the bounding box will be (-halfWidthMeters, -halfHeightMeters) to (halfWidthMeters, halfHeightMeters) with the resultant image centered at the origin.

Asynchronous downloads should be listed after synchronous ones and used for more complex sounds and images.

The file formats that can be imported are .PNG, .JPG, .BMP, and .GIF.

See Also

importImage, importImageColorKey

importImageColorKey(url, red, green, blue)

Statics Class

Enables a particular color in the synchronously imported image, specified by an RGB value, to be treated as transparent.

Syntax

public static ImageBvr importImageColorKey(
     URL url,
     ColorBvr red,
     ColorBvr green,
     ColorBvr blue
);

Parameters

url
Path (a string) of the image to be downloaded.
red
ColorBvr red component, expressed as a value ranging from 0 to 255.
green
ColorBvr green component, expressed as a value ranging from 0 to 255.
blue
ColorBvr blue component, expressed as a value ranging from 0 to 255.

Return Value

Returns the ImageBvr object.

Remarks

The file formats that can be imported are .PNG, .JPG, .BMP, and .GIF.

See Also

ImportImage

importImageColorKey(url, imgStandIn, ev, progress, size, red, green, blue)

Statics Class

Allows a particular color in the asynchronously imported image, specified by an RGB value, to be treated as transparent.

Syntax

public static ImageBvr importImage(
     URL url,
     ImageBvr imgStandIn,
     DXMEvent[] ev,
     NumberBvr[] progress,
     NumberBvr[] size,
     ColorBvr red,
     ColorBvr green,
     ColorBvr blue
);

Parameters

url
Path (a string) of the image to be downloaded.
imgStandIn
Image to use while the contents at url are still being downloaded. This argument cannot be null, however, the image can be empty.
ev
Event to trigger once the download is complete. This argument can be null.
progress
NumberBvr that goes from 0 to 1, tracking the progress of the download. For files downloaded via http, progress is reported as the percentage of the file that has been downloaded. Other protocols, the value may simply start at 0 and change to 1 once the download is complete. This argument can be null.
size
NumberBvr that is the size of the image, in bytes. The value begins at -1 and will change to the true size if it is available. This argument can be null.
red
ColorBvr red component, expressed as a value ranging from 0 to 255.
green
ColorBvr green component, expressed as a value ranging from 0 to 255.
blue
ColorBvr blue component, expressed as a value ranging from 0 to 255.

Return Value

Returns the ImageBvr object.

Remarks

The file formats that can be imported are .PNG, .JPG, .BMP, and .GIF.

See Also

importImage

importMovie(url, img, snd)

Statics Class

Downloads a movie synchronously. Synchronous loads are blocking calls, which means that ticking will not continue on any model until all synchronous downloads are completed. These downloads should be small and listed before asynchronous downloads. Use asynchronous downloads for more complex images and sounds. See below for a description of the asynchronous import method.

The image portion of a movie has the same bounding box and placement as a static image such as a PNG or JPEG.

Syntax

public static NumberBvr importMovie(
URL url, ImageBvr[] img, SoundBvr[] snd );

Parameters

url
Fully qualified path of the movie to be downloaded. This parameter is of type java.net.URL.
img
Image portion of the movie (passed by reference).
snd
Sound portion of the movie (passed by reference).

Return Value

Returns the NumberBvr object that is the length of the movie in seconds.

See Also

importImage

importMovie(url, img, snd, imgStandIn, sndStandIn, ev, progress, size)

Statics Class

Imports a movie asynchronously. Asynchronous downloads should be listed after synchronous ones and used for more complex sounds and images. For a description of the synchronous import method, see above.

The image portion of a movie has the same bounding box and placement as a static image such as a PNG or GIF.

Syntax

public static NumberBvr importMovie(
     URL url,
     ImageBvr[] img,
     SoundBvr[] snd,
     ImageBvr imgStandIn,
     SoundBvr sndStandIn,
     DXMEvent[] ev,
     NumberBvr[] progress,
     NumberBvr[] size
);

Parameters

url
Fully qualified path of the movie to download. This parameter is of type java.net.URL.
img
Image portion of the movie (passed by reference).
snd
Sound portion of the movie (passed by reference).
imgStandIn
Image to use while the movie is still being downloaded. This parameter cannot be null, however, the image can be empty.
sndStandIn
Sound to use while the movie is still being downloaded. This parameter cannot be null, however, the sound can be silence.
progress
NumberBvr that goes from 0 to 1, tracking the progress of the download. For files downloaded via http, progress is reported as the percentage of the file that has been downloaded. Other protocols, the value may simply start at 0 and change to 1 once the download is complete. This argument can be null.
size
NumberBvr that is the size of the image, in bytes. The value begins at -1 and will change to the true size if it is available. This argument can be null.

Return Value

Returns the NumberBvr object that is the length of the movie in seconds.

See Also

importImage

overlay

Statics Class

Creates an image behavior by laying the image behavior i1 over the image behavior i2. The new image consists of all fully opaque points of i1, all points of i2 that are under fully transparent points in i1, and all partially opaque points of i1 modified such that the new point is a combination of the i1 point and the i2 point under it. The bounding box of a set of overlaid images is simply the union of the bounding boxes of the images themselves.

Syntax

public static ImageBvr overlay( ImageBvr i1, ImageBvr i2 );

Parameters

i1 and i2
ImageBvr objects.

Return Value

Returns the ImageBvr object.

overlayArray

Statics Class

Creates an image behavior by layering the ImageBvr objects. The bounding box of a set of overlaid images is simply the union of the bounding boxes of the images themselves.

Syntax

public static ImageBvr overlayArray( ImageBvr[] im );

Parameters

im
ImageBvr objects in the array.

Return Value

Returns the ImageBvr object.

radialGradientPolygon

Statics Class

Creates an image behavior that is a regular polygon with a radial fill. The image contains the specified colors at the origin and the exterior points, respectively. The gradient fill is determined by the fallOff argument and occurs in RGB space. Outside of the filled-in region, the image is transparent and unpickable. The bounding box is the box surrounding the points of the polygon.

Syntax

public static ImageBvr radialGradientPolygon(
     ColorBvr inner,
     ColorBvr outer,
     Point2Bvr[] points,
     NumberBvr fallOff
);

Parameters

inner
ColorBvr object representing the color at the origin of the polygon.
outer
ColorBvr object representing the color at the vertices.
points
Point2Bvr object representing the array of points that determine the polygon's vertices.
fallOff
NumberBvr object used to control how rapidly one color transitions into another. A value of 1 is a linear transition. For 0 < power < 1 the transition occurs rapidly, while power > 1 means that the transition occurs more slowly. For example, if you are transitioning from red to blue, and power = 2, you will see a great deal of red in the gradient fill. On the other hand, if power = 0.5 (the inverse of 2), you will see very little red. If power = 0, you will see only blue. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

radialGradientRegularPoly

Statics Class

Creates an image behavior that is a regular, gradient polygon with the specified number of sides (there must be at least 3) and a radial fill. The image contains the specified colors at the origin and the exterior points, respectively. The gradient fill is determined by the fallOff argument and occurs in RGB space. Outside of the filled-in region, the image is transparent and unpickable. The bounding box is the box surrounding the points of the polygon.

Syntax

public static ImageBvr radialGradientRegularPoly(
     ColorBvr inner,
     ColorBvr outer,
     NumberBvr edges,
     NumberBvr fallOff
);

Parameters

inner
ColorBvr object representing the color at the origin of the polygon.
outer
ColorBvr object representing the color at the vertices.
edges
NumberBvr object representing the number of edges the polygon has. This parameter can also be of type double.
fallOff
NumberBvr object used to control how rapidly one color transitions into another. A value of 1 is a linear transition. For 0 < power < 1 the transition occurs rapidly, while power > 1 means that the transition occurs more slowly. For example, if you are transitioning from red to blue, and power = 2, you will see a great deal of red in the gradient fill. On the other hand, if power = 0.5 (the inverse of 2), you will see very little red. If power = 0, you will see only blue. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

radialGradientSquare

Statics Class

Creates an image behavior with a square radial fill. The image is a unit-sized square, centered at the origin, reaching from [-0.5, -0.5] to [0.5, 0.5]. The image contains the specified colors in the center and at the corners, respectively. The gradient fill is determined by the fallOff argument. The interpolation occurs in RGB space. Outside of the fill, the image is transparent and unpickable. The bounding box is a box from [-0.5, -0.5] to [0.5, 0.5].

Syntax

public static ImageBvr radialGradientSquare(
ColorBvr inner, ColorBvr outer, NumberBvr fallOff );

Parameters

inner
ColorBvr at the center of the image.
outer
ColorBvr at the corners of the image.
fallOff
NumberBvr object used to control how rapidly one color transitions into another. A value of 1 is a linear transition. For 0 < power < 1 the transition occurs rapidly, while power > 1 means that the transition occurs more slowly. For example, if you are transitioning from red to blue, and power = 2, you will see a great deal of red in the gradient fill. On the other hand, if power = 0.5 (the inverse of 2), you will see very little red. If power = 0, you will see only blue. This parameter can also be of type double.

Return Value

Returns the ImageBvr object.

solidColorImage

Statics Class

Creates an image behavior and applies the given color behavior to all points in the image. The bounding box of a solid-color image is the infinite bounding box.

Syntax

public static ImageBvr solidColorImage( ColorBvr col );

Parameters

col
ColorBvr object.

Return Value

Returns the ImageBvr object.

stringImage

Statics Class

Constructs an ImageBvr object from a StringBvr object and a FontStyleBvr object.

Syntax

public static ImageBvr stringImage(
StringBvr string, FontStyleBvr font );

Parameters

string
StringBvr object specifying the (potentially) animated text. This parameter can also be of type java.lang.String.
font
FontStyleBvr object specifying the font style (this can include the face, the size, the color, and the attributes).

Return Value

Returns the ImageBvr object.


Statics Methods Relevant to MatteBvr Objects-Java

Statics Methods Relevant to MatteBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to MatteBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ImageBvr Objects
  * Next Topic: MontageBvr Objects

Statics Methods Relevant to MatteBvr Objects

difference | Creates a matte behavior that is the difference between the given mattes.
fillMatte | Creates a matte behavior consisting of the given path behavior.
intersect | Creates a matte behavior that is the intersection of the given mattes.
textMatte | Creates a matte behavior consisting of the given StringBvr object, and with an appearance specified by the FontStyleBvr object.
union | Creates a matte behavior that is the union of the given mattes.

difference

Statics Class

Creates a matte behavior that is the difference between the given mattes. The difference consists of only those elements of the path and/or text in m1 that are not also in m2.

Syntax

public static MatteBvr difference( MatteBvr m1, MatteBvr m2 );

Parameters

m1 and m2
MatteBvr objects.

Return Value

Returns the MatteBvr object.

See Also

intersect, union

fillMatte

Statics Class

Creates a matte behavior consisting of the given path behavior. Clipping an image with this matte preserves that portion of the image that is inside the path.

Syntax

public static MatteBvr fillMatte( Path2Bvr path );

Parameters

path
Path2Bvr object. If the path is not closed, the start and end points are implicitly connected to close the path.

Return Value

Returns the MatteBvr object.

See Also

textMatte

intersect

Statics Class

Creates a matte behavior that is the intersection of the given mattes. The intersection consists of only those elements of the paths and/or text of the original mattes that are the same.

Syntax

public static MatteBvr intersect( MatteBvr m1, MatteBvr m2 );

Parameters

m1 and m2
MatteBvr objects.

Return Value

Returns the MatteBvr object.

See Also

union, difference

textMatte

Statics Class

Creates a matte behavior consisting of the given StringBvr object, and with an appearance specified by the FontStyleBvr object. Clipping an image with this matte preserves that portion of the image that is inside the text.

Syntax

public static MatteBvr textMatte( StringBvr text, FontStyleBvr fs );

Parameters

text
StringBvr object representing the text.
fs
FontStyleBvr object representing the font style.

Return Value

Returns the MatteBvr object.

See Also

fillMatte

union

Statics Class

Creates a matte behavior that is the union of the given mattes. The union consists of all elements of the paths and/or text in the original mattes.

Syntax

public static MatteBvr union( MatteBvr m1, MatteBvr m2 );

Parameters

m1 and m2
MatteBvr objects.

Return Value

Returns the MatteBvr object.

See Also

intersect, difference


Statics Methods Relevant to MontageBvr Objects-Java

Statics Methods Relevant to MontageBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to MontageBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: MatteBvr Objects
  * Next Topic: NumberBvr Objects

Statics Methods Relevant to MontageBvr Objects

imageMontage | Creates a montage behavior consisting of a single image behavior at the given z-value.
union | Creates a montage behavior by combining the given montage behaviors.

imageMontage

Statics Class

Creates a montage behavior consisting of a single image behavior at the given z-value.

Syntax

public static MontageBvr imageMontage(
ImageBvr image, NumberBvr Z-value );

Parameters

image
ImageBvr object.
Z-value
NumberBvr object. This parameter can also be of type double.

Return Value

Returns the MontageBvr object.

union

Statics Class

Creates a montage behavior by combining the given montage behaviors.

Syntax

public static MontageBvr union( MontageBvr m1, MontageBvr m2 );

Parameters

m1 and m2
MontageBvr objects.

Return Value

Returns the MontageBvr object.


Statics Methods Relevant to NumberBvr Objects-Java

Statics Methods Relevant to NumberBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to NumberBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: MontageBvr Objects
  * Next Topic: Path2Bvr Objects

Statics Methods Relevant to NumberBvr Objects

abs | Creates a number behavior that represents the absolute value of the given number behavior.
acos | Creates a number behavior that represents the arccosine (inverse cosine) of the given behavior.
add | Creates a number behavior representing the sum of the given number behaviors.
asin | Creates a number behavior that represents the arcsine (inverse sine) of the given behavior.
atan | Creates a number behavior that represents the arctangent (inverse tangent) of the given behavior.
atan2 | Creates a number behavior that represents the arctangent (inverse tangent) of the quotient of the given behaviors.
bSpline | Creates a NumberBvr behavior based on a B-spline function.
ceiling | Creates a number behavior that represents the ceiling value, the smallest integer value that is greater than or equal to the value of the given behavior.
cos | Creates a number behavior that represents the cosine of the given behavior.
degreesToRadians | Creates a number behavior that represents the value of the given behavior converted from degrees to radians.
derivative | Creates a number behavior of the instantaneous derivative (rate of change) of the given number behavior.
div | Creates an number behavior representing the quotient of the given number behaviors.
eq | Creates a Boolean behavior that represents the relation a = b.
exp | Creates a number behavior that represents the exponential of the given behavior.
floor | Creates a number behavior that represents the floor value, the largest integer value that is less than or equal to the value of the given behavior.
gt | Creates a Boolean behavior that represents the relation a > b.
gte | Creates a Boolean behavior that represents the relation a ³ b.
integral | Creates a number behavior that represents the sum of all values of a from the start time to the present.
interpolate | Creates a NumberBvr object that moves along the interval in duration seconds.
ln | Creates a number behavior that represents the natural logarithm of the given behavior.
log10 | Creates a number behavior that represents the common logarithm (base-10 logarithm) of the given behavior.
lt | Creates a Boolean behavior that represents the relation a < b.
lte | Creates a Boolean behavior that represents the relation a £ b.
mod | Creates a number behavior that represents the modulus of the given behaviors.
mul | Creates a number behavior representing the product of the given number behaviors.
ne | Creates a BooleanBvr behavior that represents the relation a != b.
neg | Creates a number behavior representing the negation of the given number behavior.
pow | Creates a number behavior representing a raised to the power of b.
radiansToDegrees | Creates a number behavior that represents the value of the given behavior converted from radians to degrees.
round | Creates a number behavior that represents the value of the given behavior, rounded to the nearest integer.
seededRandom | Creates a number behavior that represents an infinite, time-varying sequence of pseudo-random values.
sin | Creates a number behavior that represents the sine of the given behavior.
slowInSlowOut | Creates a NumberBvr object that moves along the interval in duration seconds.
sqrt | Creates a number behavior that represents the square root of the given number behavior.
sub | Creates a number behavior representing the difference of the given number behaviors.
tan | Creates a number behavior that represents the tangent of the given behavior.
toBvr | Converts a double floating-point number to a NumberBvr.

abs

Statics Class

Creates a number behavior that represents the absolute value of the given number behavior. The behavior's value is equal to the absolute value (the non-negative equivalent) of the value of a.

Syntax

public static NumberBvr abs( NumberBvr a );

Parameters

a
NumberBvr object to apply the operation to.

Return Value

Returns the NumberBvr object.

acos

Statics Class

Creates a number behavior that represents the arccosine (inverse cosine) of the given behavior.

Syntax

public static NumberBvr acos( NumberBvr a );

Parameters

a
NumberBvr object. The value of the behavior is assumed to be a cosine value.

Return Value

Returns the NumberBvr object.

See Also

asin

add

Statics Class

Creates a number behavior representing the sum of the given number behaviors. The behavior's value is the value of a added to the value of b (a + b).

Syntax

public static NumberBvr add( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to add.

Return Value

Returns the NumberBvrobject.

asin

Statics Class

Creates a number behavior that represents the arcsine (inverse sine) of the given behavior.

Syntax

public static NumberBvr asin( NumberBvr a );

Parameters

a
NumberBvr object. The value of the behavior is assumed to be a sine value.

Return Value

Returns the NumberBvr object.

See Also

acos

atan

Statics Class

Creates a number behavior that represents the arctangent (inverse tangent) of the given behavior.

Syntax

public static NumberBvr atan( NumberBvr a );

Parameters

a
NumberBvr object.

Return Value

Returns the NumberBvr object.

See Also

acos, asin, atan2

atan2

Statics Class

Creates a number behavior that represents the arctangent (inverse tangent) of the quotient of the given behaviors. The behavior's value is the inverse tangent of the value of a divided by the value of b.

Syntax

public static NumberBvr atan2( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to divide.

Return Value

Returns the NumberBvr object.

See Also

acos, asin

bSpline

Statics Class

Creates a NumberBvr behavior based on a B-spline function. The function depends on the control elements, the knot vector, the mathematical degree, and the weights of the control elements (these are optional).

Syntax

public static NumberBvr bSpline(
     int degree,
     NumberBvr[] knots,
     NumberBvr[] control_elements,
     NumberBvr[] weights,
     NumberBvr evaluation
);

Parameters

degree
Number representing the mathematical degree of the B-spline to evaluate. Must be 1, 2, or 3.
knots
Array of NumberBvr objects representing the knots used to calculate the B-spline.
control_elements
Array of NumberBvr objects representing the control elements used to calculate the B-spline.
weights
Array of NumberBvr objects representing the weights used to calculate the rational B-spline. There must be the same number of weights as control elements. For non-rational B-splines, the value of weights is null.
evaluation
NumberBvr object that controls the spline evaluation.

Return Value

Returns the NumberBvr object.

Remarks

A B-spline function without weights is piecewise polynomial and with weights is piecewise rational (the quotient of two polynomials). It requires that:

numberOfControlElements = numberOfKnots - degree + 1

The control elements define the approximate direction and shape of the function. The knots represent junction values, in parameter space, between the connected polynomial parts.

Given a degree d and a number of knots k, the function is valid in the range knotd to knotk-d+1, and is otherwise undefined. For example, for a degree 2 curve, the valid range is from the second knot to the next to the last knot, inclusive.

The returned NumberBvr is the traversal of the spline function according to the evaluation parameter. The evaluation parameter is a NumberBvr that must be in the valid range of the spline, which is [knotd, knotk-d+1]. Any values outside this range are automatically clamped to the end values.

ceiling

Statics Class

Creates a number behavior that represents the ceiling value, the smallest integer value that is greater than or equal to the value of the given behavior.

Syntax

public static NumberBvr ceiling( NumberBvr a );

Parameters

a
NumberBvr object to find the ceiling for.

Return Value

Returns the NumberBvr object.

cos

Statics Class

Creates a number behavior that represents the cosine of the given behavior.

Syntax

public static NumberBvr cos( NumberBvr a );

Parameters

a
NumberBvr object. The value of the behavior is assumed to be in radians.

Return Value

Returns the NumberBvr object.

See Also

sin

degreesToRadians

Statics Class

Creates a number behavior that represents the value of the given behavior converted from degrees to radians.

Syntax

public static NumberBvr degreesToRadians( NumberBvr a );

Parameters

a
NumberBvr object to convert.

Return Value

Returns the NumberBvr object.

derivative

Statics Class

Creates a number behavior of the instantaneous derivative (rate of change) of the given number behavior. For example, a constant number yields a constant derivative of 0 and a value equal to "time" yields a constant derivative of 1.

Syntax

public static NumberBvr derivative( NumberBvr a );

Parameters

a
NumberBvr object for which to calculate the derivative.

Return Value

Returns the NumberBvr object.

div

Statics Class

Creates an number behavior representing the quotient of the given number behaviors. The behavior's value is the value of a divided by the value of b (a/b).

Syntax

public static NumberBvr div( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to divide.

Return Value

Returns NumberBvr the object.

eq

Statics Class

Creates a boolean behavior that represents the relation a = b. The behavior's value is true when the value of a is equal to the value of b, otherwise, it is false.

Syntax

public static BooleanBvr eq( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to compare.

Return Value

Returns the BooleanBvr object.

exp

Statics Class

Creates a number behavior that represents the exponential of the given behavior. The behavior's value is the base of natural logarithms, e, raised to the power of the value of a.

Syntax

public static NumberBvr exp( NumberBvr a );

Parameters

a
NumberBvr object to apply as an exponent of e.

Return Value

Returns the NumberBvr object.

See Also

ln

floor

Statics Class

Creates a number behavior that represents the floor value, the largest integer value that is less than or equal to the value of the given behavior.

Syntax

public static NumberBvr floor( NumberBvr a );

Parameters

a
NumberBvr object to find the floor for.

Return Value

Returns the NumberBvr object.

gt

Statics Class

Creates a boolean behavior that represents the relation a > b. The behavior's value is true when the value of a is greater than the value of b, otherwise, it is false.

Syntax

public static BooleanBvr gt( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to compare.

Return Value

Returns the BooleanBvr object.

gte

Statics Class

Creates a boolean behavior that represents the relation a ³ b. The behavior's value is true when the value of a is greater than or equal to the value of b, otherwise, it is false.

Syntax

public static BooleanBvr gte( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to compare.

Return Value

Returns the BooleanBvr object.

integral

Statics Class

Creates a number behavior that represents the sum of all values of a from the start time to the present.

Syntax

public static NumberBvr integral( NumberBvr a );

Parameters

a
NumberBvr object to integrate.

Return Value

Returns the NumberBvr object.

interpolate

Statics Class

Creates a NumberBvr object that moves along the interval in duration seconds. Velocity is constant.

Syntax

public static NumberBvr interpolate(
NumberBvr from, NumberBvr to, NumberBvr duration );

Parameters

from
Beginning NumberBvr object. This parameter can also be of type double.
to
Ending NumberBvr object. This parameter can also be of type double.
duration
NumberBvr object representing the length of time, in seconds. This parameter can also be of type double.

Return Value

Returns the NumberBvr object.

ln

Statics Class

Creates a number behavior that represents the natural logarithm of the given behavior. The behavior's value is the value by which the base of natural logarithms, e, must be raised to equal the value of a.

Syntax

public static NumberBvr ln( NumberBvr a );

Parameters

a
NumberBvr object for which to calculate the natural logarithm.

Return Value

Returns the NumberBvr object.

See Also

exp

log10

Statics Class

Creates a number behavior that represents the common logarithm (base-10 logarithm) of the given behavior. The behavior's value is the value by which 10 must be raised to equal the value of a.

Syntax

public static NumberBvr log10( NumberBvr a );

Parameters

a
NumberBvr object for which to calculate the common logarithm.

Return Value

Returns the NumberBvr object.

lt

Statics Class

Creates a Boolean behavior that represents the relation a < b. The behavior's value is true when the value of a is less than the value of b, otherwise, it is false.

Syntax

public static BooleanBvr lt( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to compare.

Return Value

Returns the BooleanBvr object.

lte

Statics Class

Creates a Boolean behavior that represents the relation a £ b. The behavior's value is true when the value of a is less than or equal to the value of b, otherwise, it is false.

Syntax

public static BooleanBvr lte( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to compare.

Return Value

Returns the BooleanBvr object.

mod

Statics Class

Creates a number behavior that represents the modulus of the given behaviors. The behavior's value is the modulus, the remainder of the floating-point value of a divided by the floating-point value of b.

Syntax

public static NumberBvr mod( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to calculate the modulus for.

Return Value

Returns the NumberBvr object.

Remarks

If a=7.6 and b=3.1, then mod(a,b)= 2.4, since 3.1 will go into 7.6 evenly two times, with a remainder of 2.4.

mul

Statics Class

Creates a number behavior representing the product of the given number behaviors. The behavior's value is the value of a multiplied by the value of b (a ´ b).

Syntax

public static NumberBvr mul( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to multiply.

Return Value

Returns the NumberBvr object.

ne

Statics Class

Creates a BooleanBvr behavior that represents the relation a != b. The behavior's value is true when the value of a is not equal to the value of b, otherwise, it is false.

Syntax

public static BooleanBvr ne( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to compare.

Return Value

Returns the BooleanBvr object.

neg

Statics Class

Creates a number behavior representing the negation of the given number behavior. The behavior's value is equal to the value of a subtracted from zero (0 - a).

Syntax

public static NumberBvr neg( NumberBvr a );

Parameters

a
NumberBvr object to negate.

Return Value

Returns the NumberBvr object.

pow

Statics Class

Creates a number behavior representing a raised to the power of b. The behavior's value is equal to ab.

Syntax

public static NumberBvr pow( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to apply the operation to.

Return Value

Returns the NumberBvr object.

radiansToDegrees

Statics Class

Creates a number behavior that represents the value of the given behavior converted from radians to degrees.

Syntax

public static NumberBvr radiansToDegrees( NumberBvr a );

Parameters

a
NumberBvr object to convert.

Return Value

Returns the NumberBvr object.

round

Statics Class

Creates a number behavior that represents the value of the given behavior, rounded to the nearest integer.

Syntax

public static NumberBvr round( NumberBvr a );

Parameters

a
NumberBvr object to be rounded.

seededRandom

Statics Class

Creates a number behavior that represents an infinite, time-varying sequence of pseudo-random values.

Syntax

public static NumberBvr seededRandom( double seed );

Parameters

seed
The double value that provides the seed, or starting value, for the sequence.

Return Value

Returns the NumberBvr object.

sin

Statics Class

Creates a number behavior that represents the sine of the given behavior.

Syntax

public static NumberBvr sin( NumberBvr a );

Parameters

a
NumberBvr object. The value of the behavior is assumed to be in radians.

Return Value

Returns the NumberBvr object.

See Also

cos

slowInSlowOut

Statics Class

Creates a NumberBvr object that moves along the interval in duration seconds. The animation is dampened so that the number increases slowly, hits a maximum speed at the midpoint, and then decelerates until the end is reached. The acceleration is specified in Gs, or multiples of the earth's gravitational constant.

Syntax

public static NumberBvr slowInSlowOut(
     NumberBvr from,
     NumberBvr to,
     NumberBvr duration,
     NumberBvr acceleration
);

Parameters

from
NumberBvr object that is the beginning of the animation. This parameter can also be of type double.
to
NumberBvr object that is the end of the animation. This parameter can also be of type double.
duration
NumberBvr object that is the length of the animation, in seconds. This parameter can also be of type double.
acceleration
NumberBvr object that is the rate of acceleration, in Gs. This parameter can also be of type double.

Return Value

Returns the NumberBvr object.

sqrt

Statics Class

Creates a number behavior that represents the square root of the given number behavior. The behavior's value is the square root of the value of a.

Syntax

public static NumberBvr sqrt( NumberBvr a );

Parameters

a
NumberBvr object to apply the square root operation to.

Return Value

Returns the NumberBvr object.

sub

Statics Class

Creates a number behavior representing the difference of the given number behaviors. The behavior's value is the value of b subtracted from a (a - b).

Syntax

public static NumberBvr sub( NumberBvr a, NumberBvr b );

Parameters

a and b
NumberBvr objects to subtract.

Return Value

Returns the NumberBvr object.

tan

Statics Class

Creates a number behavior that represents the tangent of the given behavior.

Syntax

public static NumberBvr tan( NumberBvr a );

Parameters

a
NumberBvr object. The value of the behavior is assumed to be in radians.

Return Value

Returns the NumberBvr object.

See Also

atan, cos, sin

toBvr

Statics Class

Converts a double floating-point number to a NumberBvr. The only time toBvr is used is when converting Java numbers, Booleans, or strings to their corresponding DirectAnimation types. It is not used for constants such as red, yVector3, and origin2 because these are defined as constant behaviors.

Syntax

public static NumberBvr toBvr( double a );

Parameters

a
The double floating-point number to be converted.

Return Value

Returns the NumberBvr object.


Statics Methods Relevant to Path2Bvr Objects-Java

Statics Methods Relevant to Path2Bvr Objects DirectAnimation Animated Header --Statics Methods Relevant to Path2Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: NumberBvr Objects
  * Next Topic: Point2Bvr Objects

Statics Methods Relevant to Path2Bvr Objects

arc | Creates a Path2Bvr object representing a segment of an oval (in effect, an arc).
arcDegrees | Creates a Path2Bvr object representing a segment of an oval (in effect, this is an arc).
concat | Creates a path behavior that represents the concatenation of the given path behaviors.
concatArray | Creates a path behavior that represents the concatenation of the Path2Bvr objects in the array.
cubicBSplinePath | Creates a Path2Bvr behavior based on a cubic B-spline curve.
line | Creates a path behavior that represents the line segment defined by the given point behaviors.
oval | Creates a Path2Bvr object that represents an oval, centered about the origin, and whose width and height are described by the given NumberBvr arguments.
pie | Creates a Path2Bvr object representing a closed path that traces a segment of an oval and connects it to the origin. The arc of the oval is specified in radians.
pieDegrees | Creates a Path2Bvr object representing a closed path that traces a segment of an oval and connects it to the origin. The arc of the oval is specified in degrees.
polydrawPath | Creates a Path2Bvr object by manipulating the array of points with the codes contained in the NumberBvr array.
polyline | Creates a path behavior that consists of the given point behaviors.
ray | Creates a path behavior that represents the line segment from the origin to the given point behavior.
rect | Creates the Path2Bvr object representing a rectangle, centered at the origin.
roundRect | Creates a Path2Bvr object that is a rectangle, centered about the origin, with rounded, rather than square, corners.
stringPath | Creates a Path2Bvr object from the supplied StringBvr object.

arc

Statics Class

Creates a Path2Bvr object representing a segment of an oval (in effect, an arc). The oval is centered at the origin.

Syntax

public static Path2Bvr arc(
     NumberBvr startAngle,
     NumberBvr endAngle,
     NumberBvr arcWidth,
     NumberBvr arcHeight
);

Parameters

startAngle
NumberBvr object representing the starting angle of the arc, expressed in radians. This parameter can also be of type double.
endAngle
NumberBvr object representing the ending angle of the arc, expressed in radians. This parameter can also be of type double.
arcWidth
NumberBvr object representing the width of the arc, expressed in meters. This parameter can also be of type double.
arcHeight
NumberBvr object representing the height of the arc, expressed in meters. This parameter can also be of type double.

Return Value

Returns the Path2Bvr object.

arcDegrees

Statics Class

Creates a Path2Bvr object representing a segment of an oval (in effect, this is an arc). The oval is centered at the origin.

Syntax

public static Path2Bvr arcDegrees(
     double startAngle,
     double endAngle,
     double arcWidth,
     double arcHeight
);

Parameters

startAngle
The double value representing the starting angle of the oval segment, expressed in degrees.
endAngle
The double representing the ending angle of the oval segment, expressed in degrees.
arcWidth
The double representing the width of the arc, expressed in meters.
arcHeight
The double representing the height of the arc, expressed in meters.

Return Value

Returns the Path2Bvr object.

concat

Statics Class

Creates a path behavior that represents the concatenation of the given path behaviors. In the new path, the points of p1 occur first, followed by those of p2. Path p2 is transformed so that the first point of p2 aligns with the last point of p1.

Syntax

public static Path2Bvr concat( Path2Bvr p1, Path2Bvr p2 );

Parameters

p1 and p2
Path2Bvr objects.

Return Value

Returns the Path2Bvr object.

concatArray

Statics Class

Creates a path behavior that represents the concatenation of the Path2Bvr objects in the array. In the new path, the points of the first path in the array occur first, followed sequentially by the other paths in the array. The second path, for example, is transformed so that its first point aligns with the last point of the first path.

Syntax

public static Path2Bvr concatArray( Path2Bvr[] paths );

Parameters

paths
Array of Path2Bvr objects.

Return Value

Returns the Path2Bvr object.

cubicBSplinePath

Statics Class

Creates a Path2Bvr behavior based on a cubic B-spline curve.

Syntax

public static Path2Bvr cubicBSplinePath(
Point2Bvr[] points, NumberBvr[] knots );

Parameters

points
Array of Point2Bvr objects representing the points used to calculate the B-spline.
knots
Array of NumberBvr objects representing the knots used to calculate the B-spline.

Return Value

Returns the Path2Bvr object.

Remarks

The curve depends on the control points and the knot vector, and requires that:

numberofPoints = numberofKnots - 2

The control points define the approximate direction and shape of the function. The knots represent the junction values, in parameter space, between the connected polynomial parts. For a number of knots k, the function is valid in the range [3, knotk-2], and is otherwise undefined.

line

Statics Class

Creates a path behavior that represents the line segment defined by the given point behaviors.

Syntax

public static Path2Bvr line( Point2Bvr p1, Point2Bvr p2 );

Parameters

p1 and p2
Point2Bvr objects.

Return Value

Returns the Path2Bvrobject.

oval

Statics Class

Creates a Path2Bvr object that represents an oval, centered about the origin, and whose width and height are described by the given NumberBvr arguments.

Syntax

public static Path2Bvr oval( NumberBvr width, NumberBvr height );

Parameters

x-radius
NumberBvr object representing the oval's width. This parameter can also be of type double.
y-radius
NumberBvr object representing the oval's height. This parameter can also be of type double.

Return Value

Returns the Path2Bvr object.

pie

Statics Class

Creates a Path2Bvr object representing a closed path that traces a segment of an oval and connects it to the origin. (In effect, it is a pie-shaped wedge). The oval is centered at the origin. The arc of the oval is specified in radians.

Syntax

public static Path2Bvr pie(
     NumberBvr startAngle,
     NumberBvr endAngle,
     NumberBvr arcWidth, arcHeight
);

Parameters

startAngle
NumberBvr object representing the starting angle of the oval segment, expressed in radians. This parameter can also be of type double.
endAngle
NumberBvr object representing the ending angle of the oval segment, expressed in radians. This parameter can also be of type double.
arcWidth
NumberBvr object representing the width of the arc, expressed in meters. This parameter can also be of type double.
arcHeight
NumberBvr object representing the height of the arc, expressed in meters. This parameter can also be of type double.

Return Value

Returns the Path2Bvr object.

pieDegrees

Statics Class

Creates a Path2Bvr object representing a closed path that traces a segment of an oval and connects it to the origin. (In effect, it is a pie-shaped wedge). The oval is centered at the origin. The arc of the oval is specified in degrees.

Syntax

public static Path2Bvr pieDegrees(
     double startAngle,
     double endAngle,
     double arcWidth,
     double arcHeight
);

Parameters

startAngle
The double value representing the starting angle of the oval segment, expressed in degrees.
endAngle
The double representing the ending angle of the oval segment, expressed in degrees.
arcWidth
The double representing the width of the arc, expressed in meters.
arcHeight
The double representing the height of the arc, expressed in meters.

Return Value

Returns the Path2Bvr object.

polydrawPath

Statics Class

Creates a Path2Bvr object by manipulating the array of points with the codes contained in the NumberBvr array.

Syntax

public static Path2Bvr polydrawPath(
Point2Bvr[] points, NumberBvr[] codes );

Parameters

points
Array of Point2Bvr objects.
codes
Array of NumberBvr objects representing the instruction codes.

Return Value

Returns the Path2Bvr object.

Remarks

The codes represent the instructions that can be combined with a logical OR operation, where:

Code | Name | Meaning
01 | PT_CLOSEFIGURE | Connects points to create a closed figure.
02 | PT_LINETO | Connects points in a line.
04 | PT_BEZIERTO | Connects points in a Bezier curve. These must occur in multiples of 3.
06 | PT_MOVETO | Moves to a point. This is, implicitly, always the first instruction.

This function is equivalent to the Graphics Device Interface (GDI) polydraw function.

polyline

Statics Class

Creates a path behavior that consists of the given point behaviors. The path can be used either for drawing or animation. The default line style is the defaultLineStyle.

Syntax

public static Path2Bvr polyline( Point2Bvr[] points );

Parameters

points
Array of Point2Bvr objects.

Return Value

Returns the Path2Bvr object.

ray

Statics Class

Creates a path behavior that represents the line segment from the origin to the given point behavior.

Syntax

public static Path2Bvr ray( Point2Bvr p );

Parameters

p
Point2Bvr object.

Return Value

Returns the Path2Bvrobject.

rect

Statics Class

Creates the Path2Bvr object representing a rectangle, centered at the origin.

Syntax

public static Path2Bvr rect( NumberBvr width, NumberBvr height );

Parameters

width
NumberBvr object representing the width of the rectangle. This parameter can also be of type double.
height
NumberBvr object representing the height of the rectangle. This parameter can also be of type double.

Return Value

Returns the Path2Bvr object.

roundRect

Statics Class

Creates a Path2Bvr object that is a rectangle, centered about the origin, with rounded, rather than square, corners. The corners are considered to be segments of an oval (in effect, an arc), centered at the origin.

Syntax

public static Path2Bvr roundRect(
     NumberBvr width,
     NumberBvr height,
     NumberBvr arcWidth,
     NumberBvr arcHeight
);

Parameters

width
NumberBvr object representing the rectangle's width. This parameter can also be of type double.
height
NumberBvr object representing the rectangle's height. This parameter can also be of type double.
arcWidth
NumberBvr object representing the width of the arc, expressed in meters. This parameter can also be of type double.
arcHeight
NumberBvr object representing the height of the arc, expressed in meters. This parameter can also be of type double.

Return Value

Returns the Path2Bvr object.

stringPath

Statics Class

Creates a Path2Bvr object from the supplied StringBvr object. The appearance is specified by the FontStyleBvr object. The path behavior is useful for creating a matte to which an image behavior can be clipped.

Syntax

public static Path2Bvr stringPath( StringBvr string, FontStyleBvr fs );

Parameters

string
StringBvr object used to create the Path2Bvr object. This parameter can also be of type java.lang.String.
fs
FontStyleBvr object specifying the appearance of the string.

Return Value

Returns the Path2Bvr object.

See Also

draw, fill


Statics Methods Relevant to Point2Bvr Objects-Java

Statics Methods Relevant to Point2Bvr Objects DirectAnimation Animated Header --Statics Methods Relevant to Point2Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Path2Bvr Objects
  * Next Topic: Point3Bvr Objects

Statics Methods Relevant to Point2Bvr Objects

add | Creates a two-dimensional point behavior.
bSpline | Creates a Point2Bvr behavior based on a B-spline function.
derivative | Creates a two-dimensional vector behavior of the instantaneous derivative (rate of change) of the given point behavior.
distance | Creates a number behavior representing the distance between two point behaviors.
distanceSquared | Creates a number behavior representing the square of the distance between two point behaviors.
point2 | Creates a two-dimensional point behavior.
point2Polar | Creates a two-dimensional point behavior. The location of the point is given in polar coordinates.
sub(p1, p2) | Creates a two-dimensional vector behavior.
sub(pt, vec) | Creates a two-dimensional point behavior. The location of the point is calculated by subtracting the coordinates of the given vector behavior from the corresponding coordinates of the given point behavior.

add

Statics Class

Creates a two-dimensional point behavior. The location of the point is calculated by adding the corresponding coordinates of the given point and vector behaviors.

Syntax

public static Point2Bvr add( Point2Bvr pt, Vector2Bvr vec );

Parameters

pt
Point2Bvr object.
vec
Vector2Bvr object.

Return Value

Returns the Point2Bvr object.

See Also

sub

bSpline

Statics Class

Creates a Point2Bvr behavior based on a B-spline function. The function depends on the control elements, the knot vector, the mathematical degree, and the weights of the control elements (these are optional).

Syntax

public static Point2Bvr bSpline(
     int degree,
     NumberBvr[] knots,
     Point2Bvr[] control_elements,
     NumberBvr[] weights,
     NumberBvr evaluation
);

Parameters

degree
Number representing the mathematical degree of the B-spline to evaluate. Must be 1, 2, or 3.
knots
Array of NumberBvr objects representing the knots used to calculate the B-spline.
control_elements
Array of Point2Bvr objects representing the control elements used to calculate the B-spline.
weights
Array of NumberBvr objects representing the weights used to calculate the rational B-spline. There must be the same number of weights as control elements. For non-rational B-splines, the value of weights is null.
evaluation
NumberBvr object that controls the spline evaluation.

Return Value

Returns the Point2Bvr object.

Remarks

A B-spline function without weights is piecewise polynomial and with weights is piecewise rational (the quotient of two polynomials). It requires that:

numberOfControlElements = numberOfKnots - degree + 1

The control elements define the approximate direction and shape of the function. The knots represent junction values, in parameter space, between the connected polynomial parts.

Given a degree d and a number of knots k, the function is valid in the range knotd to knotk-d+1, and is otherwise undefined. For example, for a degree 2 curve, the valid range is from the second knot to the next to the last knot, inclusive.

The returned Point2Bvr is the traversal of the spline function according to the evaluation parameter. The evaluation parameter is a NumberBvr that must be in the valid range of the spline, which is [knotd, knotk-d+1]. Any values outside this range are automatically clamped to the end values.

derivative

Statics Class

Creates a two-dimensional vector behavior of the instantaneous derivative (rate of change) of the given point behavior. For example, a constant yields a constant derivative of [0 0], and point2 (time, 0) (without additional time substitutions) yields a constant derivative of [1 0].

Syntax

public static Vector2Bvr derivative( Point2Bvr pt );

Parameters

pt
Point2Bvr object.

Return Value

Returns the Vector2Bvr object.

distance

Statics Class

Creates a number behavior representing the distance between two point behaviors.

Syntax

public static NumberBvr distance( Point2Bvr p1, Point2Bvr p2 );

Parameters

p1 and p2
Point2Bvr objects.

Return Value

Returns the NumberBvr object.

See Also

distanceSquared

distanceSquared

Statics Class

Creates a number behavior representing the square of the distance between two point behaviors. This is the same as the distance method except that the distance is squared.

Syntax

public static NumberBvr distanceSquared(
Point2Bvr p1, Point2Bvr p2 );

Parameters

p1 and p2
Point2Bvr objects.

Return Value

Returns the NumberBvr object.

See Also

distance

point2

Statics Class

Creates a two-dimensional point behavior. The location of the point is given in Cartesian coordinates.

Syntax

public static Point2Bvr point2( NumberBvr x, NumberBvr y );

Parameters

x and y
NumberBvr objects, specifying the coordinate values. The x specifies the distance along the x-axis from the origin to the point, and y specifies the distance along the y-axis. These parameters can also be of type double.

Return Value

Returns the Point2Bvr object.

See Also

point2Polar

point2Polar

Statics Class

Creates a two-dimensional point behavior. The location of the point is given in polar coordinates.

Syntax

public static Point2Bvr point2Polar(
NumberBvr XYangle, NumberBvr length );

Parameters

XYangle
NumberBvr object specifying the angle, in radians, between the positive x-axis and the ray that goes from the origin to the point. This parameter can also be of type double.
length
NumberBvr object specifying the distance from the origin to the point. This parameter can also be of type double.

Return Value

Returns the Point2Bvr object.

See Also

point2

sub(p1, p2)

Statics Class

Creates a two-dimensional vector behavior. The coordinate values of the vector are calculated by subtracting the coordinates of p2 from the corresponding coordinates of p1.

Syntax

public static Vector2Bvr sub( Point2Bvr p1, Point2Bvr p2 );

Parameters

p1 and p2
Point2Bvr objects.

Return Value

Returns the Vector2Bvr object.

sub(pt, vec)

Statics Class

Creates a two-dimensional point behavior. The location of the point is calculated by subtracting the coordinates of the given vector behavior from the corresponding coordinates of the given point behavior.

Syntax

public static Point2Bvr sub( Point2Bvr pt, Vector2Bvr vec );

Parameters

pt
Point2Bvr object.
vec
Vector2Bvr object.

Return Value

Returns the Point2Bvr object.

See Also

add


Statics Methods Relevant to Point3Bvr Objects-Java

Statics Methods Relevant to Point3Bvr Objects DirectAnimation Animated Header --Statics Methods Relevant to Point3Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Point2Bvr Objects
  * Next Topic: SoundBvr Objects

Statics Methods Relevant to Point3Bvr Objects

add | Creates a three-dimensional point behavior. The location of the point is calculated by adding the corresponding coordinates of the given point and vector behaviors.
bSpline | Creates a Point3Bvr behavior based on a B-spline function.
derivative | Creates a three-dimensional vector behavior of the instantaneous derivative (rate of change) of the given point behavior.
distance | Creates a number behavior representing the distance between two point behaviors.
distanceSquared | Creates a number behavior representing the square of the distance between two point behaviors.
point3 | Creates a three-dimensional point behavior. The location of the point is given in Cartesian coordinates.
point3Spherical | Creates a three-dimensional point behavior. The location of the point is given in spherical coordinates.
sub(p1, p2) | Creates a three-dimensional vector behavior.
sub(pt, vec) | Creates a three-dimensional point behavior. The location of the point is calculated by subtracting the coordinates of the given vector behavior from the corresponding coordinates of the given point behavior.

add

Statics Class

Creates a three-dimensional point behavior. The location of the point is calculated by adding the corresponding coordinates of the given point and vector behaviors.

Syntax

public static Point3Bvr add( Point3Bvr pt, Vector3Bvr vec );

Parameters

pt
Point3Bvr object.
vec
Vector3Bvr object.

Return Value

Returns the Point3Bvr object.

See Also

sub

bSpline

Statics Class

Creates a Point3Bvr behavior based on a B-spline function. The function depends on the control elements, the knot vector, the mathematical degree, and the weights of the control elements (these are optional).

Syntax

public static Point3Bvr bSpline(
     int degree,
     NumberBvr[] knots,
     Point3Bvr[] control_elements,
     NumberBvr[] weights,
     NumberBvr evaluation
);

Parameters

degree
Number representing the mathematical degree of the B-spline to evaluate. Must be 1, 2, or 3.
knots
Array of NumberBvr objects representing the knots used to calculate the B-spline.
control_elements
Array of Point3Bvr objects representing the control elements used to calculate the B-spline.
weights
Array of NumberBvr objects representing the weights used to calculate the B-spline. There must be the same number of weights as control elements. For non-rational B-splines, the value of weights is null.
evaluation
NumberBvr object that controls the spline evaluation.

Return Value

Returns the Point3Bvr object.

Remarks

A B-spline function without weights is piecewise polynomial and with weights is piecewise rational (the quotient of two polynomials). It requires that:

numberOfControlElements = numberOfKnots - degree + 1

The control elements define the approximate direction and shape of the function. The knots represent junction values, in parameter space, between the connected polynomial parts.

Given a degree d and a number of knots k, the function is valid in the range knotd to knotk-d+1, and is otherwise undefined. For example, for a degree 2 curve, the valid range is from the second knot to the next to the last knot, inclusive.

The returned Point3Bvr is the traversal of the spline function according to the evaluation parameter. The evaluation parameter is a NumberBvr that must be in the valid range of the spline, which is [knotd, knotk-d+1]. Any values outside this range are automatically clamped to the end values.

derivative

Statics Class

Creates a three-dimensional vector behavior of the instantaneous derivative (rate of change) of the given point behavior. For example, a constant yields a constant derivative of [0 0 0], and point3 (time, 0) (without additional time substitutions) yields a constant derivative of [1 0 0].

Syntax

public static Vector3Bvr derivative( Point3Bvr pt );

Parameters

pt
Point3Bvr object.

Return Value

Returns the Vector3Bvr object.

distance

Statics Class

Creates a number behavior representing the distance between two point behaviors.

Syntax

public static NumberBvr distance( Point3Bvr p1, Point3Bvr p2 );

Parameters

p1 and p3
Point3Bvr objects.

Return Value

Returns the NumberBvr object.

See Also

distanceSquared

distanceSquared

Statics Class

Creates a number behavior representing the square of the distance between two point behaviors. This is the same as the distance method except that the distance is squared.

Syntax

public static NumberBvr distanceSquared(
Point3Bvr p1, Point3Bvr p2 );

Parameters

p1 and p3
Point3Bvr objects.

Return Value

Returns the NumberBvr object.

Remarks

This method is slightly more efficient than the distance method.

See Also

distance

point3

Statics Class

Creates a three-dimensional point behavior. The location of the point is given in Cartesian coordinates.

Syntax

public static Point3Bvr point3(
NumberBvr x, NumberBvr y, NumberBvr z );

Parameters

x, y, and z
NumberBvr objects, specifying the coordinate values. The x specifies the distance along the x-axis from the origin to the point, y specifies the distance along the y-axis, and z the distance along the z-axis. These parameters can also be of type double.

Return Value

Returns the Point3Bvr object.

See Also

point3Spherical

point3Spherical

Statics Class

Creates a three-dimensional point behavior. The location of the point is given in spherical coordinates.

Syntax

public static Point3Bvr point3Spherical(
     NumberBvr XYangle,
     NumberBvr YZangle,
     NumberBvr length
);

Parameters

XYangle
NumberBvr object of the counter-clockwise XY angle, in radians, of the point about the positive y-ray. The angle begins at the positive z-ray, so that [0 0 1] has a XYangle of 0, and [1 0 0] has a XYangle of pi/2. This parameter can also be of type double.
YZangle
NumberBvrobject of the counter-clockwise XY angle object of the counter-clockwise elevation angle, in radians, of the point above the XZ-plane. A YZangle of pi/2 means the point is straight up (on the positive y-ray), and a YZangle of -pi/2 is straight down (on the negative y-ray). This parameter can also be of type double.
length
NumberBvrobject of the counter-clockwise XY angle object of the distance of the point from the origin. This parameter can also be of type double.

Return Value

Returns the Point3Bvr object.

Remarks

The following table correlates Cartesian coordinates with polar coordinates (X = Don't Care).
Cartesian coordinates | XYangle | YZangle | Length
[0 0 0] | X | X | 0
[d 0 0] | pi/2 | 0 | d
[-d 0 0] | -pi/2 | 0 | d
[0 d 0] | X | pi/2 | d
[0 -d 0] | X | -pi/2 | d
[0 0 d] | 0 | 0 | d
[0 0 -d] | pi | 0 | d

See Also

point3

sub(p1, p2)

Statics Class

Creates a three-dimensional vector behavior. The coordinate values of the vector are calculated by subtracting the coordinates of p2 from the corresponding coordinates of p1.

Syntax

public static Vector3Bvr sub( Point3Bvr p1, Point3Bvr p2 );

Parameters

p1 and p2
Point3Bvr objects.

Return Value

Returns the Vector3Bvr object.

sub(pt, vec)

Statics Class

Creates a three-dimensional point behavior. The location of the point is calculated by subtracting the coordinates of the given vector behavior from the corresponding coordinates of the given point behavior.

Syntax

public static Point3Bvr sub( Point3Bvr pt, Vector3Bvr vec );

Parameters

pt
Point3Bvr object.
vec
Vector3Bvr object.

Return Value

Returns the Point3Bvr object.

See Also

add


Statics Methods Relevant to SoundBvr Objects-Java

Statics Methods Relevant to SoundBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to SoundBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Point3Bvr Objects
  * Next Topic: StringBvr Objects

Statics Methods Relevant to SoundBvr Objects

importSound(url, soundLength) | Creates a sound behavior by synchronously importing the given .wav or MIDI file.
importSound(url, soundLength, sndStandIn, ev, progress, size) | Creates a SoundBvr by asynchronously importing the given .wav or MIDI file.
mix | Creates a sound behavior that is equivalent to sound1 + sound2.
mixArray | Creates a sound behavior that is equivalent to sound1 + sound2 + soundn.

importSound(url, soundLength)

Statics Class

Creates a sound behavior by synchronously importing the given .wav or MIDI file. The soundLength parameter receives the length of the sound. These downloads should be small and listed before asynchronous downloads. Use asynchronous downloads for more complex images and sounds. See below for a description of the asynchronous import method.

Syntax

public static SoundBvr importSound(
URL url, NumberBvr[] soundLength );

Parameters

url
File path.
soundLength
NumberBvr array that receives the length of the sound in seconds. An array is used because this is an output parameter, and the system writes into the first element of the array. In Java, this is the simplest way to pass information back to the caller if you are already using the return value, which, in this case, is the sound).

Return Value

Returns the SoundBvr object.

importSound(url, soundLength, sndStandIn, ev, progress, size)

Statics Class

Creates a SoundBvr by asynchronously importing the given .wav or MIDI file. Asynchronous downloads should be listed after synchronous ones and used for more complex sounds and images. For a description of the synchronous import method, see above.

Syntax

public static SoundBvr importSound(
     URL url,
     NumberBvr[] soundLength,
     SoundBvr sndStandIn,
     DXMEvent[] ev,
     NumberBvr[] progress,
     NumberBvr[] size
);

Parameters

url
Fully qualified path of the file. It is of type java.net.URL.
soundLength
Length, in seconds, of the sound.
sndStandIn
Sound to use while the contents at url are still being downloaded. This argument cannot be null, however, the sound can be silence.
ev
Event to trigger once the download is complete. This argument can be null. Using this event is probably the best way to determine when the download is complete. (Otherwise, the Java constant Double.POSITIVE_INFINITY must be extracted.
progress
NumberBvr that goes from 0 to 1, tracking the progress of the download. For files downloaded via http, progress is reported as the percentage of the file that has been downloaded. Other protocols, the value may simply start at 0 and change to 1 once the download is complete. This argument can be null.
size
NumberBvr that is the size of the sound, in bytes. The value begins at -1 and will change to the true size if it is available. This argument can be null.

Return Value

Returns the SoundBvr object.

mix

Statics Class

Creates a sound behavior that is equivalent to sound1 + sound2.

Syntax

public static SoundBvr mix( SoundBvr sound1, SoundBvr sound2 );

Parameters

sound1 and sound2
SoundBvr objects.

Return Value

Returns the SoundBvr object.

mixArray

Statics Class

Creates a sound behavior that is equivalent to sound1 + sound2 + soundn.

Syntax

public static SoundBvr mixArray( SoundBvr[] sounds );

Parameters

sounds
SoundBvr objects in the array.

Return Value

Returns the SoundBvr object.


Statics Methods Relevant to StringBvr Objects-Java

Statics Methods Relevant to StringBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to StringBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: SoundBvr Objects
  * Next Topic: Transform2Bvr Objects

Statics Methods Relevant to StringBvr Objects

concat | Creates a string behavior representing the concatenation of the given behaviors.
toBvr | Converts a type of Java.lang.String to a StringBvr.

concat

Statics Class

Creates a string behavior representing the concatenation of the given behaviors. The behavior's value is the character string consisting of the value of b appended to the end of the value of a.

Syntax

public static StringBvr concat( StringBvr a, StringBvr b );

Parameters

a and b
StringBvr objects to concatenate.

Return Value

Returns the StringBvr object.

toBvr

Statics Class

Converts a type of Java.lang.String to a StringBvr. The only time toBvr is used is when converting Java numbers, Booleans, or strings to their corresponding DirectAnimation types. It is not used for constants such as red, yVector3, and origin2 because these are defined as constant behaviors.

Syntax

public static StringBvr toBvr( String string );

Parameters

string
String type to be converted.

Return Value

Returns the StringBvr object;


Statics Methods Relevant to Transform2Bvr Objects-Java

Statics Methods Relevant to Transform2Bvr Objects DirectAnimation Animated Header --Statics Methods Relevant to Transform2Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: StringBvr Objects
  * Next Topic: Transform3Bvr Objects

Statics Methods Relevant to Transform2Bvr Objects

compose | Creates a two-dimensional transformation behavior that is a composition of the given transformation behaviors.
compose2Array | Creates a two-dimensional transformation behavior that is a composition of the Transform2Bvr objects included in the array.
followPath | Creates a Transform2Bvr object that is a translation (from the origin) that follows a 2-D vector drawing path.
followPathAngle | Creates a Transform2Bvr object that is equal to the angle of the path's tangent while it is being traversed, translated by its position. This is equivalent to rotating the position by its derivative.
followPathAngleUpright | Creates a Transform2Bvr object that is equal to the angle of the path's tangent while it is being traversed, translated by its position. This is equivalent to rotating the position by its derivative, except that the angle that is perpendicular to the X-axis (in local coordinates) will always be less than 180 degrees.
rotate | Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, rotates it by the given angle (expressed in radians).
rotateDegrees | Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, rotates it by the given angle (expressed in degrees).
rotateRate | Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, rotates it by the given angle (expressed in radians).
rotateRateDegrees | Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, rotates it by the given angle (expressed in degrees).
scale(sx, sy) | Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, scales it. Scaling is specified as two integers.
scale(v) | Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, scales it. The scaling is specified as a Vector2Bvr object.
scaleRate | Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, scales it. The scaling rates are determined by multiplying each argument by localTime.
scale2 | Creates a two-dimensional transformation behavior that applies a uniform scale to every coordinate of a two-dimensional object.
scale2Rate | Creates a two-dimensional transformation behavior that applies a uniform scale to every coordinate of a two-dimensional object. The scaling rate is determined by multiplying the argument by localTime.
transform3x2 | Creates a two-dimensional transformation behavior that consists of a 3 by 2 matrix having the values specified by the given number behaviors.
translate(tx, ty) | Creates a two-dimensional transformation behavior that, when applied to a point behavior, translates (moves) the point by the amount given by the number behaviors.
translate(loc) | Creates a two-dimensional transformation that, when applied to a point, translates (moves) the point to the new location loc.
translate(v) | Creates a two-dimensional transformation behavior that when applied to a point behavior translates (moves) the point by the amount given by the vector behavior.
translateRate | Creates a two-dimensional transformation behavior that, when applied to a point, translates (moves) the point by the amount given by the arguments.
xShear | Creates a two-dimensional transformation behavior that shears the x-axis either up or down.
xShearRate | Creates a two-dimensional transformation behavior that shears the x-axis either up or down. The shear rate is determined by multiplying the argument by localTime.
yShear | Creates a two-dimensional transformation behavior that shears the y-axis either left or right.
yShearRate | Creates a two-dimensional transformation behavior that shears the y-axis either left or right. The shear rate is determined by multiplying the argument by localTime.

compose

Statics Class

Creates a two-dimensional transformation behavior that is a composition of the given transformation behaviors.

Syntax

public static Transform2Bvr compose(
Transform2Bvr xf1, Transform2Bvr xf2 );

Parameters

xf1 and xf2
Transform2Bvr objects.

Return Value

Returns the Transform2Bvr object.

Remarks

Composing a transform is equivalent to first applying xf2 and then applying xf1. In other words:

im.transform(compose(xf1, xf2)
== im.transform(xf2).transform(xf1)

compose2Array

Statics Class

Creates a two-dimensional transformation behavior that is a composition of the Transform2Bvr objects included in the array.

Syntax

public static Transform2Bvr compose2Array(
Transform2Bvr[] xforms );

Parameters

xforms
Transform2Bvr objects.

Return Value

Returns the Transform2Bvr object.

Remarks

Composing a transform is equivalent to first applying the nth transform, then applying the (nth-1) transform, and so on.

followPath

Statics Class

Creates a Transform2Bvr object that is a translation (from the origin) that follows a 2-D vector drawing path. The Path2Bvr object is converted into the equivalent translation transform.

Syntax

public static Transform2Bvr followPath(
Path2Bvr path, NumberBvr evaluator );

Parameters

path
Path2Bvr object that will be traced.
evaluator
NumberBvr object representing an evaluator with a value in the range [0, 1]. This value is equivalent to a position on the path, where 0 is the beginning of the path and 1 is the end. This parameter can also be of type double. If it is, then the parameter is treated as a duration, representing the amount of time it will take (in seconds) to trace the entire path.

Return Value

Returns the Transform2Bvr object.

followPathAngle

Statics Class

Creates a Transform2Bvr object that is equal to the angle of the path's tangent while it is being traversed, translated by its position. This is equivalent to rotating the position by its derivative.

Syntax

public static Transform2Bvr followPathAngle(
Path2Bvr path, NumberBvr evaluator );

Parameters

path
Path2Bvr object this is traversed.
evaluator
NumberBvr object representing an evaluator with a value in the range [0, 1]. This value is equivalent to a position on the path, where 0 is the beginning of the path and 1 is the end. This parameter can also be of type double. If it is, then the parameter is treated as a duration, representing the amount of time it will take (in seconds) to trace the entire path.

Return Value

Returns the Transform2Bvr object.

followPathAngleUpright

Statics Class

Creates a Transform2Bvr object that is equal to the angle of the path's tangent while it is being traversed, translated by its position. This is equivalent to rotating the position by its derivative, except that the angle that is perpendicular to the X-axis (in local coordinates) will always be less than 180 degrees.

Syntax

public static Transform2Bvr followPathAngleUpright(
Path2Bvr path, NumberBvr evaluator );

Parameters

path
Path2Bvr object that is traversed.
evaluator
NumberBvr object representing an evaluator with a value in the range [0, 1]. This value is equivalent to a position on the path, where 0 is the beginning of the path and 1 is the end. This parameter can also be of type double. If it is, then the parameter is treated as a duration, representing the amount of time it will take (in seconds) to trace the entire path.

Return Value

Returns the Transform2Bvr object.

rotate

Statics Class

Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, rotates it by the given angle (expressed in radians).

Syntax

public static Transform2Bvr rotate( NumberBvr radians );

Parameters

radians
NumberBvr object representing the angle of rotation. This parameter can also be of type double.

Return Value

Returns the Transform2Bvr object.

Remarks

Rotations are counter-clockwise.

rotateDegrees

Statics Class

Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, rotates it by the given angle (expressed in degrees).

Syntax

public static Transform2Bvr rotateDegrees( double degrees );

Parameters

degrees
The double representing the angle of rotation (expressed in degrees).

Return Value

Returns the Transform2Bvr object.

Remarks

Rotations are counter-clockwise.

rotateRate

Statics Class

Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, rotates it by the given angle (expressed in radians). This value is multiplied by localTime to determine the rotation rate.

Syntax

public static Transform2Bvr rotateRate( double radians );

Parameters

radians
The double value representing the angle of rotation, multiplied by localTime. The unit is radians per second.

Return Value

Returns the Transform2Bvr object.

Remarks

Rotations are counter-clockwise.

rotateRateDegrees

Statics Class

Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, rotates it by the given angle (expressed in degrees). This value is multiplied by localTime to determine the rotation rate.

Syntax

public static Transform2Bvr rotateRateDegrees( double degrees );

Parameters

degrees
The double value representing the angle of rotation, multiplied by localTime. The unit is degrees per second.

Return Value

Returns the Transform2Bvr object.

Remarks

Rotations are counter-clockwise.

scale(sx, sy)

Statics Class

Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, scales it. For example, the transformation modifies the length of a vector by multiplying the vector's coordinates by the values of the corresponding number behaviors. The scaling is specified as two integers, one for scaling along the x dimension and one for scaling along the y dimension.

Syntax

public static Transform2Bvr scale( NumberBvr sx, NumberBvr sy );

Parameters

sx and sy
NumberBvr objects representing the amount to scale. The value of sx applies to the x coordinate, and sy to the y coordinate. These parameters can also be of type double.

Return Value

Returns the Transform2Bvr object.

scale(v)

Statics Class

Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, scales it. For example, the transformation modifies the location of a point by multiplying the point's coordinates by the values of the corresponding vector coordinates. The scaling is specified as a Vector2Bvr object.

Syntax

public static Transform2Bvr scale( Vector2Bvr v );

Parameters

v
Vector2Bvr object. The x and y coordinates of the vector behavior specify the amount to scale the x and y coordinates, respectively.

Return Value

Returns the Transform2Bvr object.

scaleRate

Statics Class

Creates a two-dimensional transformation behavior that, when applied to any two-dimensional object, scales it. The scaling rates are determined by multiplying each argument by localTime.

Syntax

public static Transform2Bvr scaleRate( double x, double y );

Parameters

sx and sy
The doubles representing the amounts to scale. The value of sx applies to the x coordinate, and sy to the y coordinate.

Return Value

Returns the Transform2Bvr object.

scale2

Statics Class

Creates a two-dimensional transformation behavior that applies a uniform scale to every coordinate of a two-dimensional object.

Syntax

public static Transform2Bvr scale2( NumberBvr uniformFactor );

Parameters

uniformFactor
NumberBvr object representing the value to apply to both the x and y coordinates, respectively. This parameter can also be of type double.

Return Value

Returns the Transform2Bvr object.

See Also

scale, scale

scale2Rate

Statics Class

Creates a two-dimensional transformation behavior that applies a uniform scale to every coordinate of a two-dimensional object. The scaling rate is determined by multiplying the argument by localTime.

Syntax

public static Transform2Bvr scale2Rate( double rate );

Parameters

rate
The double representing the value to apply to both the x and y coordinates, respectively.

Return Value

Returns the Transform2Bvr object.

transform3x2

Statics Class

Creates a two-dimensional transformation behavior that consists of a 3 by 2 matrix having the values specified by the given number behaviors.

Syntax

public static Transform2Bvr transform3x2( NumberBvr[] matrix );

Parameters

matrix
A 3 by 2 matrix. The top row of the matrix could be represented as containing NumberBvr objects a00, a01, and a02. Use a00 to scale the x coordinate and a02 to translate x.
Bottom row of the matrix could be represented as containing the NumberBvr objects a10, a11, and a12. Use a11 to scale the y coordinate and a12 to translate y.

Return Value

Returns the Transform2Bvr object.

Remarks

The X and Y translation components are given by a02 and a12, respectively.

translate(tx, ty)

Statics Class

Creates a two-dimensional transformation behavior that, when applied to a point behavior, translates (moves) the point by the amount given by the number behaviors.

Syntax

public static Transform2Bvr translate( NumberBvr tx, NumberBvr ty );

Parameters

tx and ty
NumberBvr objects representing the amount to translate the point along the x and y axes, respectively. These parameters can also be of type double.

Return Value

Returns the Transform2Bvr object.

Remarks

Two-dimensional vectors are unchanged by translations.

translate(loc)

Statics Class

Creates a two-dimensional transformation that, when applied to a point, translates (moves) the point to the new location loc.

Syntax

public static Transform2Bvr translate( Point2Bvr loc );

Parameters

loc
Point2Bvr object specifying the new location.

Return Value

Returns the Transform2Bvr object.

translate(v)

Statics Class

Creates a two-dimensional transformation behavior that when applied to a point behavior translates (moves) the point by the amount given by the vector behavior.

Syntax

public static Transform2Bvr translate( Vector2Bvr v );

Parameters

v
Vector2Bvr object. The x and y coordinates of the vector behavior specify the amount to translate along the x and y axes, respectively.

Return Value

Returns the Transform2Bvr object.

Remarks

Two-dimensional vectors are unchanged by translations.

translateRate

Statics Class

Creates a two-dimensional transformation behavior that, when applied to a point, translates (moves) the point by the amount given by the arguments. The translation rates are determined by multiplying the arguments by localTime.

Syntax

public static Transform2Bvr translateRate( double x, double y );

Parameters

x and y
The doubles representing the amounts to translate the point along the X and Y axes, respectively.

Return Value

Returns the Transform2Bvr object.

Remarks

Two-dimensional vectors are unchanged by translations.

xShear

Statics Class

Creates a two-dimensional transformation behavior that shears the x-axis either up or down. This means the X-coordinate of the points remains unchanged, and the Y-coordinate increases by the product of the X-coordinate and the shear factor. In other words, X2 = X1, and Y2 = Y1 + aX1, where a is the shear factor.

Syntax

public static Transform2Bvr xShear( NumberBvr x );

Parameters

x
NumberBvr object representing the amount to shear. This parameter can also be of type double.

Return Value

Returns the Transform2Bvr object.

See Also

yShear

xShearRate

Statics Class

Creates a two-dimensional transformation behavior that shears the x-axis either up or down. The shear rate is determined by multiplying the argument by localTime.

Syntax

public static Transform2Bvr xShearRate( double rate );

Parameters

rate
The double value representing the amount to shear, multiplied by localTime. The unit is meters per second.

Return Value

Returns the Transform2Bvr object.

yShear

Statics Class

Creates a two-dimensional transformation behavior that shears the y-axis either left or right. This means the Y-coordinate of the points remains unchanged, and the X-coordinate increases by the product of the Y-coordinate and the shear factor. In other words, Y2 = Y1 and X2 = X1 + aY1, where a is the shear factor.

Syntax

public static Transform2Bvr yShear( NumberBvr y );

Parameters

y
NumberBvr object representing the amount to shear. This parameter can also be of type double.

Return Value

Returns the Transform2Bvr object.

See Also

xShear

yShearRate

Statics Class

Creates a two-dimensional transformation behavior that shears the y-axis either left or right. The shear rate is determined by multiplying the argument by localTime.

Syntax

public static Transform2Bvr yShearRate( double rate );

Parameters

rate
The double value representing the amount to shear the y-axis, multiplied by localTime. The unit is meters per second.

Return Value

Returns the Transform2Bvr object.


Statics Methods Relevant to Transform3Bvr Objects-Java

Statics Methods Relevant to Transform3Bvr Objects DirectAnimation Animated Header --Statics Methods Relevant to Transform3Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Transform2Bvr Objects
  * Next Topic: TupleBvr Objects

Statics Methods Relevant to Transform3Bvr Objects

compose | Creates a three-dimensional transformation behavior that is a composition of the given transformation behaviors.
compose3Array | Creates a three-dimensional transformation behavior that is a composition of the Transform3Bvr objects in the array.
lookAtFrom | Creates a three-dimensional transformation behavior that transforms an object from a standard position and orientation to the "from" point, and aimed at the "to" point, with "up" mapping to the object's up direction.
rotate | Creates a three-dimensional transformation behavior that, when applied to a point or vector behavior, rotates the point or vector by the given angle (expressed in radians) around the given axis.
rotateDegrees | Creates a three-dimensional transformation behavior that, when applied to a point or vector behavior, rotates the point or vector by the given angle (expressed in degrees) around the given axis.
rotateRate | Creates a three-dimensional transformation behavior that, when applied to a point or vector behavior, rotates the point or vector by the given angle (expressed in radians) around the given axis.
rotateRateDegrees | Creates a three-dimensional transformation behavior that, when applied to a point or vector behavior, rotates the point or vector by the given angle (expressed in degrees) around the given axis.
scale(sx, sy, sz) | Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it. Scaling is specified as three integers.
scale(v) | Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it. The scaling is specified as a Vector3Bvr object.
scaleRate | Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it. The scaling rates are determined by multiplying the arguments by localTime.
scale3 | Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it uniformly.
scale3Rate | Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it uniformly. The scaling rate is determined by multiplying the argument by localTime.
transform4x4 | Creates a three-dimensional transformation behavior that consists of the 4 by 4 matrix having the values specified by the given number behaviors.
translate(tx, ty, tz) | Creates a three-dimensional transformation behavior that, when applied to a point behavior, translates (moves) the point by the amounts given by the number behaviors.
translate(loc) | Creates a two-dimensional transformation that, when applied to a point, translates (moves) the point to the new location loc.
translate(v) | Creates a three-dimensional transformation behavior that, when applied to a point behavior, translates (moves) the point by the amounts given by the vector behavior.
translateRate | Creates a three-dimensional transformation behavior that, when applied to a point behavior, translates (moves) the point by the amounts given by the doubles.
xShear | Shears the x-axis in the positive y-direction by a and in the positive z-direction by b.
xShearRate | Shears the x-axis in the positive y-direction by a and in the positive z-direction by b.
yShear | Shears the y-axis in the positive x-direction by c and in the positive z-direction by d.
yShearRate | Shears the y-axis in the positive x-direction by c and in the positive z-direction by d.
zShear | Shears the z-axis in the positive x-direction by e and in the positive y-direction by f.
zShearRate | Shears the z-axis in the positive x-direction by e and in the positive y-direction by f.

compose

Statics Class

Creates a three-dimensional transformation behavior that is a composition of the given transformation behaviors.

Syntax

public static Transform3Bvr compose(
Transform3Bvr xf1, Transform3Bvr xf2 );

Parameters

xf1 and xf2
Transform3Bvr objects.

Return Value

Returns the Transform3Bvr object.

Remarks

Composing a transform is equivalent to first applying xf2 and then applying xf1. In other words:

geo.transform(compose(xf1, xf2)
== geo.transform(xf2).transform(xf1)

compose3Array

Statics Class

Creates a three-dimensional transformation behavior that is a composition of the Transform3Bvr objects in the array.

Syntax

public static Transform3Bvr compose3Array(
Transform3Bvr[] xforms );

Parameters

xf1 and xf2
Transform3Bvr objects.

Return Value

Returns the Transform3Bvr object.

Remarks

Composing a transform is equivalent to first applying the nth transform, then applying the (nth-1) transform, and so on.

lookAtFrom

Statics Class

Creates a three-dimensional transformation behavior that transforms an object from a standard position and orientation to the "from" point, and aimed at the "to" point, with "up" mapping to the object's up direction. Prior to this transform, objects must be located at the origin, with the up side aligned with positive y, and aimed in the negative z-direction.

Syntax

public static Transform3Bvr lookAtFrom(
Point3Bvr from, Point3Bvr to, Vector3Bvr up );

Parameters

from
Point3Bvr object specifying the point from which to view.
to
Point3Bvr object specifying the point toward which to view.
up
Point3Bvr object specifying the point determining the upright position.

Return Value

Returns the Transform3Bvr object.

Remarks

This transform is useful for positioning and orienting cameras, lights, microphones, and other three-dimensional objects.

rotate

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a point or vector behavior, rotates the point or vector by the given angle (expressed in radians) around the given axis.

Syntax

public static Transform3Bvr rotate(
Vector3Bvr axis, NumberBvr radians );

Parameters

axis
Vector3Bvr object representing the axis around which to rotate. The plane of rotation is perpendicular to the ray defined by this vector.
radians
NumberBvr object representing the angle of rotation. This parameter can also be of type double.

Return Value

Returns the Transform3Bvr object.

Remarks

Rotations obey the right-hand rule. This means that, when looking down the axis vector, they are counter-clockwise.

rotateDegrees

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a point or vector behavior, rotates the point or vector by the given angle (expressed in degrees) around the given axis.

Syntax

public static Transform3Bvr rotateDegrees(
Vector3Bvr axis, double degrees );

Parameters

axis
Vector3Bvr object representing the axis around which to rotate. The plane of rotation is perpendicular to the ray defined by this vector.
degrees
The double value representing the angle of rotation (expressed in degrees).

Return Value

Returns the Transform3Bvr object.

Remarks

Rotations obey the right-hand rule. This means that, when looking down the axis vector, they are counter-clockwise.

rotateRate

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a point or vector behavior, rotates the point or vector by the given angle (expressed in radians) around the given axis. The rotation rate is determined by multiplying the angle by localTime.

Syntax

public static Transform3Bvr rotateRate(
Vector3Bvr axis, double radians );

Parameters

axis
Vector3Bvr object representing the axis around which to rotate. The plane of rotation is perpendicular to the ray defined by this vector.
radians
The double value representing the angle of rotation, multiplied by localTime. The unit is radians per second.

Return Value

Returns the Transform3Bvr object.

Remarks

Rotations obey the right-hand rule. This means that, when looking down the axis vector, they are counter-clockwise.

rotateRateDegrees

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a point or vector behavior, rotates the point or vector by the given angle (expressed in degrees) around the given axis. The rotation rate is determined by multiplying the angle by localTime.

Syntax

public static Transform3Bvr rotateRateDegrees(
Vector3Bvr axis, double degrees );

Parameters

axis
Vector3Bvr object representing the axis around which to rotate. The plane of rotation is perpendicular to the ray defined by this vector.
degrees
The double value representing the angle of rotation (expressed in degrees) multiplied by localTime. The unit is degrees per second.

Return Value

Returns the Transform3Bvr object.

Remarks

Rotations obey the right-hand rule. This means that, when looking down the axis vector, they are counter-clockwise.

scale(sx, sy, sz)

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it. For example, the transformation modifies the length of a vector by multiplying the vector's coordinates by the values of the corresponding number behaviors. The scaling is specified as three integers, for scaling along the x, y, and z dimensions.

Syntax

public static Transform3Bvr scale(
NumberBvr sx, NumberBvr sy, NumberBvr sz );

Parameters

sx, sy, and sz
NumberBvr objects representing the amounts to scale. The value of sx applies to the x coordinate, sy to the y coordinate, and sz to the z coordinate. These parameters can also be of type double.

Return Value

Returns the Transform3Bvr object.

scale(v)

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it. For example, the transformation modifies the location of a point by multiplying the point's coordinates by the values of the corresponding vector coordinates. The scaling is specified as a Vector3Bvr object.

Syntax

public static Transform3Bvr scale(
Vector3Bvr v );

Parameters

v
Vector3Bvr object. The x, y, and z coordinates of the vector behavior specify the amounts to scale the x, y, and z coordinates, respectively.

Return Value

Returns the Transform3Bvr object.

scaleRate

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it. The scaling rates are determined by multiplying the arguments by localTime.

Syntax

public Transform3Bvr scaleRate(
double x, double y, double z );

Parameters

x, y, and z
The doubles representing the amounts to scale. The value of x applies to the x coordinate, y to the y coordinate, and z to the z coordinate.

Return Value

Returns the Transform3Bvr object.

scale3

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it uniformly.

Syntax

public static Transform3Bvr scale3(
NumberBvr uniformFactor );

Parameters

uniformFactor
NumberBvr object representing the value to apply to the x, y, and z coordinates, respectively. This parameter can also be of type double.

Return Value

Returns the Transform3Bvr object.

See Also

scale, scale

scale3Rate

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a three-dimensional object, scales it uniformly. The scaling rate is determined by multiplying the argument by localTime.

Syntax

public Transform3Bvr scale3Rate( double rate );

Parameters

rate
The double value to apply to the x, y, and z coordinates, respectively, multiplied by localTime. The unit is degrees per second.

Return Value

Returns the Transform3Bvr object.

transform4x4

Statics Class

Creates a three-dimensional transformation behavior that consists of the 4 by 4 matrix having the values specified by the given number behaviors.

Syntax

public static Transform3Bvr transform4x4( NumberBvr[] matrix );

Parameters

matrix
The 4 by 4 matrix. The first row of the matrix can be represented by the NumberBvr objects a00, a01, a02, a03. Use a00 to scale the x coordinate and a03 to translate x.
The second row of the matrix can be represented by the NumberBvr objects a10, a11, a12, a13. Use a11 to scale the y coordinate and a13 to translate y.
The third row of the matrix can be represented by the NumberBvr objects a20, a21, a22, a23. Use a22 to scale the z coordinate and a23 to translate z.
The fourth row of the matrix can be represented by the NumberBvr objects a30, a31, a32, a33.

Return Value

Returns the Transform3Bvr object.

Remarks

The translation components for the matrix are in the last column. The last row of the matrix must be [0 0 0 1].

translate(tx, ty, tz)

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a point behavior, translates (moves) the point by the amounts given by the number behaviors.

Syntax

public static Transform3Bvr translate(
NumberBvr tx, NumberBvr ty, NumberBvr tz );

Parameters

tx, ty, and tz
NumberBvr objects representing the amounts to translate the point along the x, y, and z axes, respectively. These parameters can also be of type double.

Return Value

Returns the Transform3Bvr object.

Remarks

Vectors are unchanged by translations.

translate(loc)

Statics Class

Creates a two-dimensional transformation that, when applied to a point, translates (moves) the point to the new location loc.

Syntax

public static Transform3Bvr translate( Point3Bvr loc );

Parameters

loc
Point3Bvr object specifying the new location.

Return Value

Returns the Transform3Bvr object.

translate(v)

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a point behavior, translates (moves) the point by the amounts given by the vector behavior.

Syntax

public static Transform3Bvr translate( Vector3Bvr v );

Parameters

v
Vector3Bvr object. The x, y, and z coordinates of the vector behavior specify the amounts to translate along the x, y, and z axes, respectively.

Return Value

Returns the Transform3Bvr object.

Remarks

Vectors are unchanged by translations.

translateRate

Statics Class

Creates a three-dimensional transformation behavior that, when applied to a point behavior, translates (moves) the point by the amounts given by the doubles. The translation rate is determined by multiplying the arguments by localTime.

Syntax

public Transform3Bvr translateRate( double x, double y, double z );

Parameters

x, y, and z
The doubles representing the amounts to translate the point along the x, y, and z axes, respectively.

Return Value

Returns the Transform3Bvr object.

Remarks

Vectors are unchanged by translations.

xShear

Statics Class

Shears the x-axis in the positive y-direction by a and in the positive z-direction by b. The X-coordinate remains unchanged, and the Y and Z coordinates increase by the product of the X-coordinate and the shear factors. In other words, X2 = X1, Y2 = Y1 + aX1, and Z2 = Z1 + bX1, where a and b are the shear factors.

Syntax

public static Transform3Bvr xShear( NumberBvr a, NumberBvr b );

Parameters

a
NumberBvr object representing the amount of shear in the Y direction. This parameter can also be of type double.
b
NumberBvr object representing the amount of shear in the Z direction. This parameter can also be of type double.

Return Value

Returns the Transform3Bvr object.

See Also

yShear, zShear

xShearRate

Statics Class

Shears the x-axis in the positive y-direction by a and in the positive z-direction by b. The shear rates are determined by multiplying the arguments by localTime.

Syntax

public static Transform3Bvr xShearRate( double a, double b );

Parameters

a
The double value representing the amount of shear in the y-direction, multiplied by localTime. The unit is meters per second.
b
The double value representing the amount of shear in the z-direction, multiplied by localTime. The unit is meters per second.

Return Value

Returns the Transform3Bvr object.

See Also

yShear, zShear

yShear

Statics Class

Shears the y-axis in the positive x-direction by c and in the positive z-direction by d. The Y-coordinate remains unchanged, and the X and Z coordinates increase by the product of the Y-coordinate and the shear factors. In other words, Y2 = Y1, X2 = X1 + cY1, and Z2 = Z1 + dY1, where c and d are the shear factors.

Syntax

public static Transform3Bvr yShear( NumberBvr c, NumberBvr d );

Parameters

c
NumberBvr object representing the amount of shear in the X direction. This parameter can also be of type double.
d
NumberBvr object representing the amount of shear in the Z direction. This parameter can also be of type double.

Return Value

Returns the Transform3Bvr object.

See Also

xShear, zShear

yShearRate

Statics Class

Shears the y-axis in the positive x-direction by c and in the positive z-direction by d. The shear rates are determined by multiplying the arguments by localTime.

Syntax

public static Transform3Bvr yShearRate( double c, double d );

Parameters

c
The double value representing the amount of shear in the x-direction, multiplied by localTime. The unit is meters per second.
d
The double value representing the amount of shear in the z-direction, multiplied by localTime. The unit is meters per second.

Return Value

Returns the Transform3Bvr object.

See Also

xShear, zShear

zShear

Statics Class

Shears the z-axis in the positive x-direction by e and in the positive y-direction by f. The Z-coordinate remains unchanged, and the X and Y coordinates increase by the product of the Z-coordinate and the shear factors. In other words, Z2 = Z1, X2 = X1 + eZ1, and Y2 = Y1 + fZ1, where e and f are the shear factors.

Syntax

public static Transform3Bvr zShear( NumberBvr e, NumberBvr f );

Parameters

e
NumberBvr object representing the amount of shear in the X direction. This parameter can also be of type double.
f
NumberBvr object representing the amount of shear in the Y direction. This parameter can also be of type double.

Return Value

Returns the Transform3Bvr object.

See Also

xShear, yShear

zShearRate

Statics Class

Shears the z-axis in the positive x-direction by e and in the positive y-direction by f. The shear rates are determined by multiplying the arguments by localTime.

Syntax

public static Transform3Bvr zShearRate( double e, double f );

Parameters

e
The double value representing the amount of shear in the x-direction, multiplied by localTime. The unit is meters per second.
f
The double representing the amount of shear in the y-direction, multiplied by localTime. The unit is meters per second.

Return Value

Returns the Transform3Bvr object.

See Also

xShear, yShear


Statics Methods Relevant to TupleBvr Objects-Java

Statics Methods Relevant to TupleBvr Objects DirectAnimation Animated Header --Statics Methods Relevant to TupleBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Transform3Bvr Objects
  * Next Topic: Vector2Bvr Objects

Statics Methods Relevant to TupleBvr Objects

pairBvr | A convenience utility for creating a TupleBvr object with two members.
tripleBvr | A convenience utility for creating a TupleBvr object with three members.

pairBvr

Statics Class

A convenience utility for creating a TupleBvr object with two members.

Syntax

public static TupleBvr pairBvr( Behavior first, Behavior second );

Parameters

first
First Behavior object in the TupleBvr.
second
Second Behavior object in the TupleBvr.

Return Value

Returns the TupleBvr object.

tripleBvr

Statics Class

A convenience utility for creating a TupleBvr object with three members.

Syntax

public static TupleBvr tripleBvr(
Behavior first, Behavior second, Behavior third );

Parameters

first
First Behavior object in the TupleBvr.
second
Second Behavior object in the TupleBvr.
third
Third Behavior object in the TupleBvr.

Return Value

Returns the TupleBvr object.


Statics Methods Relevant to Vector2Bvr Objects-Java

Statics Methods Relevant to Vector2Bvr Objects DirectAnimation Animated Header --Statics Methods Relevant to Vector2Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: TupleBvr Objects
  * Next Topic: Vector3Bvr Objects

Statics Methods Relevant to Vector2Bvr Objects

add | Creates a two-dimensional vector behavior that is the vector-valued sum of the two vectors.
bSpline | Creates a Vector2Bvr behavior based on a B-spline function.
derivative | Creates a new, two-dimensional vector behavior that is the instantaneous derivative (rate of change) of the given vector behavior.
dot | Creates a number behavior that represent the "dot product" of the given vectors.
integral | Creates a two-dimensional vector that represents the sum of the all values of vec from the given start time to the present.
neg | Creates a new two-dimensional vector that is the same length as the original vector but points in the opposite direction.
sub | Creates a two-dimensional vector behavior.
vector2 | Creates a two-dimensional vector behavior, having a direction and length as specified by the given Cartesian coordinates.
vector2Polar | Creates a two-dimensional vector behavior, having a direction and length as specified by the given polar coordinates.
vector2PolarDegrees | Creates a two-dimensional vector behavior, having a direction and length as specified by the given polar coordinates.

add

Statics Class

Creates a two-dimensional vector behavior that is the vector-valued sum of the two vectors.

Syntax

public static Vector2Bvr add( Vector2Bvr v1, Vector2Bvr v2 );

Parameters

v1 and v2
Vector2Bvr objects.

Return Value

Returns the Vector2Bvr object.

See Also

sub

bSpline

Statics Class

Creates a Vector2Bvr behavior based on a B-spline function. The function depends on the control elements, the knot vector, the mathematical degree, and the weights of the control elements (these are optional).

Syntax

public static Vector2Bvr bSpline(
     int degree,
     NumberBvr[] knots,
     Vector2Bvr[] control_elements,
     NumberBvr[] weights,
     NumberBvr evaluation
);

Parameters

degree
Number representing the mathematical degree of the B-spline to evaluate. Must be 1, 2, or 3.
knots
Array of NumberBvr objects representing the knots used to calculate the B-spline.
control_elements
Array of Point2Bvr objects representing the control elements used to calculate the B-spline.
weights
Array of NumberBvr objects representing the weights used to calculate the rational B-spline. There must be the same number of weights as control elements. For non-rational B-splines, the value of weights is null.
evaluation
NumberBvr object that controls the spline evaluation.

Return Value

Returns the Vector2Bvr object.

Remarks

A B-spline function without weights is piecewise polynomial and with weights is piecewise rational (the quotient of two polynomials). It requires that:

numberOfControlElements = numberOfKnots - degree + 1

The control elements define the approximate direction and shape of the function. The knots represent junction values, in parameter space, between the connected polynomial parts.

Given a degree d and a number of knots k, the function is valid in the range knotd to knotk-d+1, and is otherwise undefined. For example, for a degree 2 curve, the valid range is from the second knot to the next to the last knot, inclusive.

The returned Vector2Bvr is the traversal of the spline function according to the evaluation parameter. The evaluation parameter is a NumberBvr that must be in the valid range of the spline, which is [knotd, knotk-d+1]. Any values outside this range are automatically clamped to the end values.

derivative

Statics Class

Creates a new, two-dimensional vector behavior that is the instantaneous derivative (rate of change) of the given vector behavior. For example, a constant vector behavior yields a constant derivative of [0 0], and vector2(time, 0) (without any additional time substitutions) yields a constant derivative vector of [1 0].

Syntax

public static Vector2Bvr derivative( Vector2Bvr vec );

Parameters

vec
Vector2Bvr object.

Return Value

Returns the Vector2Bvr object.

dot

Statics Class

Creates a number behavior that represent the "dot product" of the given vectors. The dot product is the product of the lengths of the vectors and the cosine of the angle between them.

Syntax

public static NumberBvr dot( Vector2Bvr v1, Vector2Bvr v2 );

Parameters

v1 and v2
Vector2Bvr objects.

Return Value

Returns the NumberBvr object.

integral

Statics Class

Creates an two-dimensional vector that represents the sum of the all values of vec from the given start time to the present.

Syntax

public static Vector2Bvr integral( Vector2Bvr vec );

Parameters

a
Vector2Bvr object.

Return Value

Returns the Vector2Bvr object.

neg

Statics Class

Creates a new two-dimensional vector that is the same length as the original vector but points in the opposite direction.

Syntax

public static Vector2Bvr neg( Vector2Bvr v1 );

Parameters

v1
Vector2Bvr object that is the original vector.

Return Value

Returns the Vector2Bvr object.

sub

Statics Class

Creates a two-dimensional vector behavior. The direction and length of the vector are calculated by subtracting the coordinates of v2 from the corresponding coordinates of v1.

Syntax

public static Vector2Bvr sub( Vector2Bvr v1, Vector2Bvr v2 );

Parameters

v1 and v2
Vector2Bvr objects.

Return Value

Returns the Vector2Bvr object.

See Also

add

vector2

Statics Class

Creates a two-dimensional vector behavior, having a direction and length as specified by the given Cartesian coordinates.

Syntax

public static Vector2Bvr vector2( NumberBvr x, NumberBvr y );

Parameters

x
NumberBvr object specifying the distance along the x-axis from the origin to a point. This parameter can also be of type double.
y
NumberBvr object specifying the distance along the y-axis. This parameter can also be of type double.

Return Value

Returns the Vector2Bvr object.

See Also

vector2Polar

vector2Polar

Statics Class

Creates a two-dimensional vector behavior, having a direction and length as specified by the given polar coordinates.

Syntax

public static Vector2Bvr vector2Polar(
NumberBvr XYangle, NumberBvr length );

Parameters

XYangle
NumberBvr object that is the counter-clockwise angle of the vector, in radians, from the positive x-ray. This parameter can also be of type double.
length
NumberBvr object that is the length of the vector. This parameter can also be of type double.

Return Value

Returns the Vector2Bvr object.

See Also

vector2

vector2PolarDegrees

Statics Class

Creates a two-dimensional vector behavior, having a direction and length as specified by the given polar coordinates.

Syntax

public static Vector2Bvr vector2PolarDegrees(
double XYangle, double length );

Parameters

XYangle
The double that is the counter-clockwise angle of the vector (expressed in degrees) from the positive x-ray.
length
The double that is the length of the vector.

Return Value

Returns the Vector2Bvr object.


Statics Methods Relevant to Vector3Bvr Objects - Java

Statics Methods Relevant to Vector3Bvr Objects DirectAnimation Animated Header --Statics Methods Relevant to Vector3Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Vector2Bvr Objects
  * Next Topic: BooleanBvr Objects

Statics Methods Relevant to Vector3Bvr Objects

add | Creates a three-dimensional vector behavior. The direction and length of the vector are calculated by adding the corresponding coordinates of the given vectors.
bSpline | Creates a Vector3Bvr behavior based on a B-spline function.
cross | Creates a vector behavior that represents the "cross product" of the given vectors.
derivative | Creates a new, three-dimensional vector behavior that is the instantaneous derivative (rate of change) of the given vector behavior.
dot | Creates a number behavior that represent the "dot product" of the given vectors.
integral | Creates a three-dimensional vector that represents the sum of the all values of vec from the given start time to the present.
neg | Creates a new three-dimensional vector behavior that has the same length as the original vector, but points in the opposite direction.
sub | Creates a three-dimensional vector behavior. The direction and length of the vector are calculated by subtracting the coordinates of v2 from the corresponding coordinates of v1.
vector3Spherical | Creates a three-dimensional vector behavior, having a direction and length as specified by the given spherical coordinates.
vector3 | Creates a three-dimensional vector behavior, having a direction and length as specified by the given Cartesian coordinates.

add

Statics Class

Creates a three-dimensional vector behavior. The direction and length of the vector are calculated by adding the corresponding coordinates of the given vectors.

Syntax

public static Vector3Bvr add( Vector3Bvr v1, Vector3Bvr v2 );

Parameters

v1 and v2
Vector3Bvr objects.

Return Value

Returns the Vector3Bvr object.

See Also

sub

bSpline

Statics Class

Creates a Vector3Bvr behavior based on a B-spline function. The function depends on the control elements, the knot vector, the mathematical degree, and the weights of the control elements (these are optional).

Syntax

public static Vector3Bvr bSpline(
     int degree,
     NumberBvr[] knots,
     Vector3Bvr[] control_elements,
     NumberBvr[] weights,
     NumberBvr evaluation
);

Parameters

degree
Number representing the mathematical degree of the B-spline to evaluate. Must be 1, 2, or 3.
knots
Array of NumberBvr objects representing the knots used to calculate the B-spline.
control_elements
Array of Vector3Bvr objects representing the control elements used to calculate the B-spline.
weights
Array of NumberBvr objects representing the weights used to calculate the rational B-spline. There must be the same number of weights as control elements. For nonrational B-splines, the value of weights is null.
evaluation
A NumberBvr object that controls the spline evaluation. See Remarks for more information.

Return Value

Returns the Vector3Bvr object.

Remarks

A B-spline function without weights is piecewise polynomial and with weights is piecewise rational (the quotient of two polynomials). It requires that:

numberOfControlElements = numberOfKnots - degree + 1

The control elements define the approximate direction and shape of the function. The knots represent junction values, in parameter space, between the connected polynomial parts.

Given a degree d and a number of knots k, the function is valid in the range knotd to knotk-d+1, and is otherwise undefined. For example, for a degree 2 curve, the valid range is from the second knot to the next to the last knot, inclusive.

The returned Vector3Bvr is the traversal of the spline function according to the evaluation parameter. The evaluation parameter is a NumberBvr that must be in the valid range of the spline, which is [knotd, knotk-d+1]. Any values outside this range are automatically clamped to the end values.

cross

Statics Class

Creates a vector behavior that represents the "cross product" of the given vectors. The new vector has a length that is equal to the product of the lengths of the original vectors and the sine of the angle between them. The new vector has a direction that is perpendicular to the plane formed by the original vectors and that obeys the right-hand rule.

Syntax

public static Vector3Bvr cross( Vector3Bvr v1, Vector3Bvr v2 );

Parameters

v1 and v2
Vector3Bvr objects.

Return Value

Returns the Vector3Bvr object.

derivative

Statics Class

Creates a new, three-dimensional vector behavior that is the instantaneous derivative (rate of change) of the given vector behavior. For example, a constant vector behavior yields a constant derivative of [0 0 0], and vector3(time, 0, 0) (without any additional time substitutions) yields a constant derivative vector of [1 0 0].

Syntax

public static Vector3Bvr derivative( Vector3Bvr vec );

Parameters

vec
Vector3Bvr object.

Return Value

Returns the Vector3Bvr object.

dot

Statics Class

Creates a number behavior that represent the "dot product" of the given vectors. The dot product is the product of the lengths of the vectors and the cosine of the angle between them.

Syntax

public static NumberBvr dot( Vector3Bvr v1, Vector3Bvr v2 );

Parameters

v1 and v2
Vector3Bvr objects.

Return Value

Returns the NumberBvr object.

integral

Statics Class

Creates an three-dimensional vector that represents the sum of the all values of vec from the given start time to the present.

Syntax

public static Vector3Bvr integral( Vector3Bvr vec );

Parameters

a
Vector3Bvr object.

Return Value

Returns the Vector3Bvr object.

neg

Statics Class

Creates a new three-dimensional vector behavior that has the same length as the original vector, but points in the opposite direction.

Syntax

public static Vector3Bvr neg( Vector3Bvr v1 );

Parameters

v1
Vector3Bvr object that is the original vector.

Return Value

Returns the Vector3Bvr object.

sub

Statics Class

Creates a three-dimensional vector behavior. The direction and length of the vector are calculated by subtracting the coordinates of v2 from the corresponding coordinates of v1.

Syntax

public static Vector3Bvr sub( Vector3Bvr v1, Vector3Bvr v2 );

Parameters

v1 and v2
Vector3Bvr objects.

Return Value

Returns the Vector3Bvr object.

See Also

add

vector3Spherical

Statics Class

Creates a three-dimensional vector behavior, having a direction and length as specified by the given spherical coordinates.

Syntax

public static Vector3Bvr vector3Spherical(
     NumberBvr XYangle,
     NumberBvr YZangle,
     NumberBvr length
);

Parameters

XYangle
NumberBvr object of the counter-clockwise XY angle, in radians, of the point about the positive y-axis. The angle begins at the positive z-ray, so that [0 0 1] has a XYangle of 0, and [1 0 0] has a XYangle of pi/2. This parameter can also be of type double.
YZangle
NumberBvr object of the counter-clockwise elevation angle, in radians, of the point above the XZ-plane. A YZangle of pi/2 means the point is straight up (on the positive y-ray), and a YZangle of -pi/2 is straight down (on the negative y-ray). This parameter can also be of type double.
length
NumberBvr object specifying the length of the vector. This parameter can also be of type double.

Return Value

Returns the Vector3Bvr object.

Remarks

The following table correlates Cartesian coordinates with polar coordinates (X = Don't Care).
Cartesian coordinates | XYangle | YZangle | Length
[0 0 0] | X | X | 0
[d 0 0] | pi/2 | 0 | d
[-d 0 0] | -pi/2 | 0 | d
[0 d 0] | X | pi/2 | d
[0 -d 0] | X | -pi/2 | d
[0 0 d] | 0 | 0 | d
[0 0 -d] | pi | 0 | d

See Also

vector3

vector3

Statics Class

Creates a three-dimensional vector behavior, having a direction and length as specified by the given Cartesian coordinates.

Syntax

public static Vector3Bvr vector3(
NumberBvr x, NumberBvr y, NumberBvr z );

Parameters

x, y, and z
NumberBvr objects specifying the coordinate values. The x specifies a distance along the x-axis from the origin to a point, y specifies a distance along the y-axis, and z a distance along the z-axis. These parameters can also be of type double.

Return Value

Returns the Vector3Bvr object.

See Also

vector3Spherical


Statics Fields Relevant to BooleanBvr Objects-Java

Statics Fields Relevant to BooleanBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to BooleanBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Vector3Bvr Objects
  * Next Topic: ColorBvr Objects

Statics Fields Relevant to BooleanBvr Objects

falseBvr
Boolean behavior (BooleanBvr object) that is always false.
leftButtonState
BooleanBvr object that is true if the left mouse button is down and false if it is up.
rightButtonState
BooleanBvr object that is true if the right mouse button is down and false if it is up.
trueBvr
Boolean behavior (BooleanBvr object) that is always true.


Statics Fields Relevant to ColorBvr Objects-Java

Statics Fields Relevant to ColorBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to ColorBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: BooleanBvr Objects
  * Next Topic: DashStyleBvr Objects

Statics Fields Relevant to ColorBvr Objects

aqua
A color behavior (ColorBvr object) that is always aqua. In a range of [0, 1], its red, green, and blue intensity values are 0.00, 1.00, 1.00. In a range of [0-255], its values are 0, 255, 255.
black
A color behavior (ColorBvr object) that is always black. In a range of [0-1], its red, green, and blue intensity values are 0.00, 0.00, 0.00. In a range of [0-255], its values are 0, 0, 0.
blue
A color behavior (ColorBvr object) that is always blue. In a range of [0-1], its red, green, and blue intensity values are 0.00, 0.00, 1.00. In a range of [0-255], its values are 0, 0, 255.
cyan
A color behavior (ColorBvr object) that is always cyan. In a range of [0-1], its red, green, and blue intensity values are 0.00, 1.00, 1.00. In a range of [0-255], its values are 0, 255, 255.
fuchsia
A color behavior (ColorBvr object) that is always fuchsia. In a range of [0-1], its red, green, and blue intensity values are 1.00, 0.00, 1.00). In a range of [0-255], its values are 255, 0, 255.
gray
A color behavior (ColorBvr object) that is always gray. In a range of [0-1], its red, green, and blue intensity values are 0.50, 0.50,0.50. In a range of [0-255], its values are 128, 128, 128.
green
A color behavior (ColorBvr object) that is always green. In a range of [0-1], its red, green, and blue intensity values are 0.00, 1.00, 0.00. In a range of [0-255], its values are 0, 255, 0.
lime
A color behavior (ColorBvr object) that is always lime. In a range of [0-1], its red, green, and blue intensity values are 0.00, 1.00, 0.00. In a range of [0-255], its values are 0, 255, 0.
magenta
A color behavior (ColorBvr object) that is always magenta. In a range of [0-1], its red, green, and blue intensity values are 1.00, 0.00, 1.00. In a range of [0-255], its values are 255, 0, 255.
maroon
A color behavior (ColorBvr object) that is always maroon. In a range of [0-1], its red, green, and blue intensity values are 0.50, 0.00, 0.00. In a range of [0-255], its values are 128, 0, 0.
navy
A color behavior (ColorBvr object) that is always navy. In a range of [0-1], its red, green, and blue intensity values are 0.00, 0.00, 0.50. In a range of [0-255], its values are 0, 0, 128.
olive
A color behavior (ColorBvr object) that is always olive. In a range of [0-1], its red, green, and blue intensity values are 0.50, 0.50, 0.00. In a range of [0-255], its values are 128, 128, 0.
purple
A color behavior (ColorBvr object) that is always purple. In a range of [0-1], its red, green, and blue intensity values are 0.50, 0.00, 0.50. In a range of [0-255], its values are 128, 0, 128.
red
A color behavior (ColorBvr object) that is always red. In a range of [0-1], its red, green, and blue intensity values are 1.00, 0.00, 0.00. In a range of [0-255], its values are 255, 0, 0.
silver
A color behavior (ColorBvr object) that is always silver. In a range of [0-1], its red, green, and blue intensity values are 0.75, 0.75, 0.75. In a range of [0-255], its values are 192, 192, 192.
teal
A color behavior (ColorBvr object) that is always teal. In a range of [0-1], its red, green, and blue intensity values are 0.00, 0.50, 0.50. In a range of [0-255], its values are 0, 128, 128.
white
A color behavior (ColorBvr object) that is always white. In a range of [0-1], its red, green, and blue intensity values are 1.00, 1.00, 1.00. In a range of [0-255], its values are 255, 255, 255.
yellow
A color behavior (ColorBvr object) that is always yellow. In a range of [0-1], its red, green, and blue intensity values are 1.00, 1.00, 0.00. In a range of [0-255], its values are 255, 255, 0.


Statics Fields Relevant to DashStyleBvr Objects-Java

Statics Fields Relevant to DashStyleBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to DashStyleBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ColorBvr Objects
  * Next Topic: DXMEvent Objects

Statics Fields Relevant to DashStyleBvr Objects

dashStyleDashed
Creates a dashstyle behavior (DashStyleBvr object) that draws a dashed line (sequence of line segments and spaces).
dashStyleSolid
Creates a dashstyle behavior (DashStyleBvr object) that draws a solid line (continuous stroke).


Statics Fields Relevant to DXMEvent Objects-Java

Statics Fields Relevant to DXMEvent Objects DirectAnimation Animated Header --Statics Fields Relevant to DXMEvent Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DashStyleBvr Objects
  * Next Topic: EndStyleBvr Objects

Statics Fields Relevant to DXMEvent Objects

always
A DXMEvent object that occurs at all times. It produces no data.
leftButtonDown
The DXMEvent object that occurs when the left mouse button is pressed. This is a state-transition event. It produces no data.
leftButtonUp
The DXMEvent object that occurs when the left mouse button is released. This is a state-transition event. It produces no data.
never
An event (DXMEvent object) that never occurs. It produces no data.
rightButtonDown
The DXMEvent object that occurs when the right mouse button is pressed. This is a state-transition event. It produces no data.
rightButtonUp
The DXMEvent object that occurs when the right mouse button is released. This is a state-transition event. It produces no data.


Statics Fields Relevant to EndStyleBvr Objects-Java

Statics Fields Relevant to EndStyleBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to EndStyleBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: DXMEvent Objects
  * Next Topic: FontStyleBvr Objects

Statics Fields Relevant to EndStyleBvr Objects

endStyleFlat
An endstyle behavior (EndStyleBvr object) that draws the end of a line as a line segment, perpendicular to the final direction of the line and through the last point of the line.
endStyleRound
An endstyle behavior (EndStyleBvr object) that draws the end of a line as a circle, having a diameter equal to the width of the line and its center at the last point of the line.
endStyleSquare
An endstyle behavior (EndStyleBvr object) that draws the end of a line as a square, having a height and width equal to the width of the line and its center at the last point of the line.


Statics Fields Relevant to FontStyleBvr Objects-Java

Statics Fields Relevant to FontStyleBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to FontStyleBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: EndStyleBvr Objects
  * Next Topic: GeometryBvr Objects

Statics Fields Relevant to FontStyleBvr Objects

defaultFont
Creates a FontStyleBvr object that is a Times Roman, black, 12 pt font that is neither bold nor italicized.


Statics Fields Relevant to GeometryBvr Objects-Java

Statics Fields Relevant to GeometryBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to GeometryBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: FontStyleBvr Objects
  * Next Topic: ImageBvr Objects

Statics Fields Relevant to GeometryBvr Objects

ambientLight
A geometry behavior (GeometryBvr object) in which the light source is ambient (room) light. The bounding box is the empty bounding box, where the maximum extent is less than the minimum extent. Ambient light is light that, instead of coming directly from a light source, bounces from surface to surface and provides overall scene-lighting. For example, if you are in a room lit by a lamp, everything not directly lit by the lamp is receiving ambient light. It leaves the lamp, bounces off the walls, the floors, the ceiling, and all around, until it reaches almost the entire room. Using too much ambient light can make a scene look washed out, while too little will yield shadowed areas that look unnaturally black. Unlike directional lights and point lights, transformations have no effect on ambient lights.
directionalLight
A geometry behavior (GeometryBvr object) in which the light source is positioned pointing down the negative z axis. The bounding box is the empty bounding box, where the maximum extent is less than the minimum extent. Directional light specifies a light such that the light direction is the same everywhere. This is in contrast to a positional light such as a point or spot light, where the direction to the light source changes depending on where the surface is. Directional lights are useful for simulating the sun because, from our perspective on earth, the sun's rays appear to travel in the same direction. Directional lights are also cheaper to compute than positional lights, though this difference may be negligible and should not be assumed. Transformations do have an effect on directional lights.
emptyGeometry
A geometry behavior (GeometryBvr object) in which all points are invisible (transparent) and undetectable. The bounding box is the empty bounding box, where the maximum coordinate is less than the minimum coordinate.
pointLight
A geometry behavior (GeometryBvr object) in which the light source is positioned at the origin and radiates light in all directions. The bounding box is the empty bounding box, where the maximum extent is less than the minimum extent. Pointlights simulate most real-world lights such as bare light bulbs, wall-mounted light fixtures, and ceiling lights. They shine in all directions, radiating outward from the center. Transformations do have an effect on pointlights.


Statics Fields Relevant to ImageBvr Objects-Java

Statics Fields Relevant to ImageBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to ImageBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: GeometryBvr Objects
  * Next Topic: JoinStyleBvr Objects

Statics Fields Relevant to ImageBvr Objects

detectableEmptyImage
An image behavior (ImageBvr object) in which all points are transparent but detectable. This image is useful for blocking mouse input from an underlying image, allowing that input to be redirected. The bounding box is of infinite extent.
emptyImage
An image behavior (ImageBvr object) in which all points are transparent and undetectable. This has an empty bounding box, which means that its maximum point is less than its minimum point. This allows operations such as union and intersection to produce the correct results.


Statics Fields Relevant to JoinStyleBvr Objects-Java

Statics Fields Relevant to JoinStyleBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to JoinStyleBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: ImageBvr Objects
  * Next Topic: LineStyleBvr Objects

Statics Fields Relevant to JoinStyleBvr Objects

joinStyleBevel
A joinstyle behavior (JoinStyleBvr object) that joins two line segments by drawing a straight line between the outside corners of the segments and filling the resulting enclosed area.
joinStyleMiter
A joinstyle behavior (JoinStyleBvr object) that joins two line segments by extending the outside edges of the segments until they intersect and filling the resulting enclosed area. The miter length is defined as the distance from the intersection of the line walls on the inside of the join to the intersection of the line walls on the outside of the join.
joinStyleRound
A joinstyle behavior (JoinStyleBvr object) that joins two line segments by drawing an arc between the outside corners of the segments and filling the resulting enclosed area. The center of the arc is at the common endpoint, the diameter is the width of the line.


Statics Fields Relevant to LineStyleBvr Objects-Java

Statics Fields Relevant to LineStyleBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to LineStyleBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: JoinStyleBvr Objects
  * Next Topic: MatteBvr Objects

Statics Fields Relevant to LineStyleBvr Objects

defaultLineStyle
A linestyle behavior (LineStyleBvr object) whose style is detail.


Statics Fields Relevant to MatteBvr Objects-Java

Statics Fields Relevant to MatteBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to MatteBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: LineStyleBvr Objects
  * Next Topic: MicrophoneBvr Objects

Statics Fields Relevant to MatteBvr Objects

clearMatte
A matte behavior (MatteBvr object) that when applied to an image preserves the entire image (that is, it clips nothing).
opaqueMatte
A matte behavior (MatteBvr object) that when applied to an image clips the entire image (that is, preserves no part of the image).


Statics Fields Relevant to MicrophoneBvr Objects-Java

Statics Fields Relevant to MicrophoneBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to MicrophoneBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: MatteBvr Objects
  * Next Topic: MontageBvr Objects

Statics Fields Relevant to MicrophoneBvr Objects

defaultMicrophone
A microphone behavior (MicrophoneBvr object) that is positioned at the origin, listening along the negative z-axis with an upward orientation along the positive y-axis.


Statics Fields Relevant to MontageBvr Objects-Java

Statics Fields Relevant to MontageBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to MontageBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: MicrophoneBvr Objects
  * Next Topic: NumberBvr Objects

Statics Fields Relevant to MontageBvr Objects

emptyMontage
A montage behavior (MontageBvr object) that contains no images.


Statics Fields Relevant to NumberBvr Objects-Java

Statics Fields Relevant to NumberBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to NumberBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: MontageBvr Objects
  * Next Topic: Point2Bvr Objects

Statics Fields Relevant to NumberBvr Objects

globalTime
A number behavior (NumberBvr object) the value of which increases at the rate of one unit per second. Unlike localTime, the globalTime value cannot be reset to zero.
localTime
A number behavior (NumberBvr object) that starts at zero when "started" and increases at the rate of one unit per second.


Statics Fields Relevant to Point2Bvr Objects-Java

Statics Fields Relevant to Point2Bvr Objects DirectAnimation Animated Header --Statics Fields Relevant to Point2Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: NumberBvr Objects
  * Next Topic: Point3Bvr Objects

Statics Fields Relevant to Point2Bvr Objects

mouseposition
A two-dimensional point behavior (Point2Bvr object) that is the current location of the mouse.
origin2
A two-dimensional point behavior (Point2Bvr object) that is always located at the origin.


Statics Fields Relevant to Point3Bvr Objects-Java

Statics Fields Relevant to Point3Bvr Objects DirectAnimation Animated Header --Statics Fields Relevant to Point3Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Point2Bvr Objects
  * Next Topic: SoundBvr Objects

Statics Fields Relevant to Point3Bvr Objects

origin3
A three-dimensional point behavior (Point3Bvr object) that is always located at the origin.


Statics Fields Relevant to SoundBvr Objects-Java

Statics Fields Relevant to SoundBvr Objects DirectAnimation Animated Header --Statics Fields Relevant to SoundBvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Point3Bvr Objects
  * Next Topic: Transform2Bvr Objects

Statics Fields Relevant to SoundBvr Objects

silence
A sound behavior (SoundBvr object) that is always completely silent (no sound).
sinSynth
A sound behavior (SoundBvr object) that represents a constant tone. This sound is synthesized using a sine wave.


Statics Fields Relevant to Transform2Bvr Objects-Java

Statics Fields Relevant to Transform2Bvr Objects DirectAnimation Animated Header --Statics Fields Relevant to Transform2Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: SoundBvr Objects
  * Next Topic: Transform3Bvr Objects

Statics Fields Relevant to Transform2Bvr Objects

identityTransform2
A two-dimensional transformation behavior (Transform2Bvr object) that leaves the two-dimensional space unchanged. Applying this transformation to a point or vector produces the same point or vector.


Statics Fields Relevant to Transform3Bvr Objects-Java

Statics Fields Relevant to Transform3Bvr Objects DirectAnimation Animated Header --Statics Fields Relevant to Transform3Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Transform2Bvr Objects
  * Next Topic: Vector2Bvr Objects

Statics Fields Relevant to Transform3Bvr Objects

identityTransform3
A three-dimensional transformation behavior (Transform3Bvr object) that leaves the three-dimensional space unchanged. Applying this transformation to a point or vector produces the same point or vector.


Statics Fields Relevant to Vector2Bvr Objects-Java

Statics Fields Relevant to Vector2Bvr Objects DirectAnimation Animated Header --Statics Fields Relevant to Vector2Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Transform3Bvr Objects
  * Next Topic: Vector3Bvr Objects

Statics Fields Relevant to Vector2Bvr Objects

xVector2
A two-dimensional vector behavior (Vector2Bvr object) that represents the unit vector on the x-axis. The vector's length is always 1, the direction is always the same as the positive x-axis.
yVector2
A two-dimensional vector behavior (Vector2Bvr object) that represents the unit vector on the y-axis. The vector's length is always 1, the direction is always the same as the positive y-axis.
zeroVector2
A two-dimensional vector behavior (Vector2Bvr object) that represents the null vector. The vector's length is always 0, the direction is undefined.


Statics Fields Relevant to Vector3Bvr Objects-Java

Statics Fields Relevant to Vector3Bvr Objects DirectAnimation Animated Header --Statics Fields Relevant to Vector3Bvr Objects * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Vector2Bvr Objects
  * Next Topic: Static Methods (Miscellaneous)

Statics Fields Relevant to Vector3Bvr Objects

xVector3
A three-dimensional vector behavior (Vector3Bvr object) that represents the unit vector on the x-axis. The vector's length is always 1, the direction is always the same as the positive x-axis.
yVector3
A three-dimensional vector behavior (Vector3Bvr object) that represents the unit vector on the y-axis. The vector's length is always 1, the direction is always the same as the positive y-axis.
zVector3
A three-dimensional vector behavior (Vector3Bvr object) that represents the unit vector on the z-axis. The vector's length is always 1, the direction is always the same as the positive z-axis.
zeroVector3
A three-dimensional vector behavior (Vector3Bvr object) that represents the null vector. The vector's length is always 0, the direction is undefined.


Statics Methods (Miscellaneous)-Java

Statics Methods (Miscellaneous) DirectAnimation Animated Header --Statics Methods (Miscellaneous) * Microsoft DirectAnimation SDK
    * Topic Contents

  * Previous Topic: Vector3Bvr Objects
  * Next Topic: Static Fields (Miscellaneous)

Statics Methods (Miscellaneous)

buildURL | Constructs a fully-qualified path from a base URL and a file path.
getVersionString | Retrieves the version string in the form MM.NN.BBBB, where MM is the major version, NN is the minor version, and BBBB is the build number.

buildURL

Statics Class

Constructs a fully-qualified path from a base URL and a file path.

Syntax

public static URL buildURL( URL url, String pathname );

Parameters

url
Base URL.
pathname
File path.

Return Value

Returns a java.net.URL object.

Example

The following code fragment constructs one URL for the directory where images are stored and another for where sounds are stored:

{
// This is a piece of a larger program
// Construct the URLs
URL mediabase = buildURL(getImportBase(),
"../../../../../../Media/");
URL imgBase = buildURL(mediabase, "image/");
URL sndBase = buildURL(mediabase, "sound/");

//Create an image behavior by importing a bitmap
ImageBvr img = importImage(buildURL(imgBase, "img1.jpg"));

//The rest of the program

}

getVersionString

Statics Class

Retrieves the version string in the form MM.NN.BBBB, where MM is the major version, NN is the minor version, and BBBB is the build number.

Syntax

public static String getVersionString( );

Return Value

Returns the String object.


Statics Fields (Miscellaneous)-Java

Statics Fields (Miscellaneous) DirectAnimation Animated Header --Statics Fields (Miscellaneous) * Microsoft DirectAnimation SDK
    * Topic Contents

  * Previous Topic: Static Methods (Miscellaneous)
  * Next Topic: StringBvr

Statics Fields (Miscellaneous)

cm
The centimeter unit. This returns a double.
foot
The foot unit. This returns a double.
inch
The inch unit. This returns a double.
meter
The meter unit. This returns a double.
mm
The millimeter unit. This returns a double.
pixelBvr
The pixel unit. This returns a NumberBvr.


StringBvr Class-Java Class

StringBvr Class DirectAnimation Animated Header --StringBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Static Fields (Miscellaneous)
  * Next Topic: Transform2Bvr

StringBvr Class

Creates an object that represents a string behavior. At any given time, the value of the behavior is a character string.

For more information about behaviors, see the Behavior class.

For relevant methods from the Statics class, see Statics Methods Relevant to StringBvr Objects.

StringBvr Methods

extract | Extracts the value of the behavior, returning a Java object.
newUninitBvr | Enables you to refer to an StringBvr behavior before that behavior has been defined.

extract

StringBvr Class

Extracts the value of the behavior, returning a Java object. This behavior must have a constant value.

Syntax

public Object extract( );

Return Value

Returns a Java object. For more information about java.lang.Object objects, consult a Java reference.

newUninitBvr

StringBvr Class

Enables you to refer to an StringBvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static StringBvr newUninitBvr( );

Return Value

Returns the StringBvr object.


Transform2Bvr Class-Java Class

Transform2Bvr Class DirectAnimation Animated Header --Transform2Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: StringBvr
  * Next Topic: Transform3Bvr

Transform2Bvr Class

Creates an object that represents a two-dimensional transformation behavior. The behavior consists of a 3 by 2 matrix of number behaviors that can be applied to two-dimensional point behaviors to translate, scale, rotate or skew the location of the point, and can be applied to two-dimensional vector behaviors to scale, rotate, or skew the length and/or direction of the vector.

Because matrix values are number behaviors (NumberBvr objects), the transformation behavior may change over time as the number behaviors change. For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to Transform2Bvr Objects
  • Statics Fields Relevant to Transform2Bvr Objects

Transform2Bvr Methods

inverse | Creates a transformation behavior that is the inverse of the original transformation.
isSingular | Creates a Boolean behavior that indicates whether the transform is singular.
newUninitBvr | Enables you to refer to a Transform2Bvr behavior before that behavior has been defined.

inverse

Transform2Bvr Class

Creates a transformation behavior that is the inverse of the original transformation.

Syntax

public Transform2Bvr inverse( );

Return Value

Returns the Transform2Bvr object.

Remarks

The inverse transformation is useful for removing the effects of the original transformation from a point or vector behavior. Some transforms have no inverse. To characterize transforms of an unknown type, use isSingular.

isSingular

Transform2Bvr Class

Creates a Boolean behavior that indicates whether the transform is singular. A singular transform is one that has no inverse.

Syntax

public BooleanBvr isSingular( );

Return Value

Returns the BooleanBvr object. This object's value is true if the transform is singular; otherwise, it is false.

newUninitBvr

Transform2Bvr Class

Enables you to refer to a Transform2Bvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Transform2Bvr newUninitBvr( );

Return Value

Returns the Transform2Bvr object.


Transform3Bvr Class-Java Class

Transform3Bvr Class DirectAnimation Animated Header --Transform3Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Transform2Bvr
  * Next Topic: TupleBvr

Transform3Bvr Class

Just as a Transform2Bvr transforms a two-dimensional object into another two-dimensional object, a Transform3Bvr transforms a three-dimensional object into another three-dimensional object. Other transforms include the render method, which transforms three-dimensional objects into two-dimensional objects; and the project method, which transforms a three-dimensional point into a two-dimensional point.

For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to Transform3Bvr Objects
  • Statics Fields Relevant to Transform3Bvr Objects

Transform3Bvr Methods

inverse | Creates a transformation behavior that is the inverse of the original transformation.
isSingular | Creates a Boolean behavior that indicates whether the transform is singular.
parallelTransform2 | Creates a Transform2Bvr object from the Transform3Bvr object.
newUninitBvr | Enables you to refer to a Transform3Bvr behavior before that behavior has been defined.

inverse

Transform3Bvr Class

Creates a transformation behavior that is the inverse of the original transformation.

Syntax

public Transform3Bvr inverse( );

Return Value

Returns the Transform3Bvr object.

Remarks

The inverse transformation is useful for removing the effects of the original transformation from a three-dimensional object. To characterize transforms of an unknown type, use isSingular.

isSingular

Transform3Bvr Class

Creates a Boolean behavior that indicates whether the transform is singular. A singular transform is one that has no inverse.

Syntax

public BooleanBvr isSingular( );

Return Value

Returns the BooleanBvr object. This object's value is true if the transform is singular; otherwise, it is false.

parallelTransform2

Transform3Bvr Class

Creates a Transform2Bvr object from the Transform3Bvr object.

Syntax

public Transform2Bvr parallelTransform2( );

Return Value

Returns the Transform2Bvr object.

Remarks

This method is equivalent to casting an orthographic projection of the Transform3Bvr onto the X-Y plane. For predictable results, the transform must be an affine (4´3) transform rather than a perspective (4´4) transform.

newUninitBvr

Transform3Bvr Class

Enables you to refer to a Transform3Bvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Transform3Bvr newUninitBvr( );

Return Value

Returns the Transform3Bvr object.


TupleBvr Class-Java Class

TupleBvr Class DirectAnimation Animated Header --TupleBvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Transform3Bvr
  * Next Topic: UntilNotifier

TupleBvr Class

A TupleBvr object is similar to an array. It can, however, accept heterogeneous types. The two methods included in this class are convenience utilities. Any number of members can be included in a TupleBvr object.

For relevant methods from the Statics class, see Statics Methods Relevant to TupleBvr Objects.

TupleBvr Methods

length | Determines the length of a tuple behavior.
nth | Selects a Behavior object based on the value of i.
newUninitBvr | Enables you to refer to a Transform3Bvr behavior before that behavior has been defined.
TupleBvr | Constructs the TupleBvr object.

length

TupleBvr Class

Determines the length of a tuple behavior.

Syntax

public int length( );

Return Value

Returns an integer. For more information about integers, consult a Java reference.

nth

TupleBvr Class

Selects a Behavior object based on the value of i.

Syntax

public Behavior nth( int i );

Parameters

i
Index into the TupleBvr object. Note that this is not a time-varying index.

newUninitBvr

TupleBvr Class

Enables you to refer to a Transform3Bvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static TupleBvr newUninitBvr( TupleBvr tuple );

Parameters

tuple
Used to establish the type the tuple holds.

Return Value

Returns the TupleBvr object.

TupleBvr

TupleBvr Class

Constructs the TupleBvr object.

Syntax

public TupleBvr( Behavior behaviors[] );

Parameters

behaviors
Behaviors to be included in the TupleBvr.


UntilNotifier Interface-Java Interface

UntilNotifier Interface DirectAnimation Animated Header --UntilNotifier Interface * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: TupleBvr
  * Next Topic: Vector2Bvr

UntilNotifier Interface

Represents the event notifier used to generate a replacement behavior for a behavior created using an untilNotify method.

UntilNotifier Methods

notify Creates a behavior in response to an event.

notify

UntilNotifier Interface

Creates a behavior in response to an event. This method is called when the event specified in a call to an untilNotify method occurs. The eventData and previous parameters specify the state of the event and the initial behavior, and can be used to determine how to create the new behavior.

Syntax

public Behavior notify(
     Object eventData,
     Behavior previous,
     BvrsToRun extraBvrsToRun
);

Parameters

eventData
Object that contains any data produced by the event itself.
previous
Behavior object that represents the running behavior from which the application is transitioning.
extraBvrsToRun
Behaviors that are not a part of the model.

Return Value

Returns the Behavior object.

See Also

untilNotify


Vector2Bvr Class-Java Class

Vector2Bvr Class DirectAnimation Animated Header --Vector2Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: UntilNotifier
  * Next Topic: Vector3Bvr

Vector2Bvr Class

Creates an object that represents a two-dimensional vector behavior. At any given time, the value of the behavior is a direction and length (magnitude), specified as a pair of coordinate values given as either Cartesian coordinates (x, y) or polar coordinates (XYangle, length). The direction of the vector is parallel to the ray that starts at the origin and passes through the point specified by the vector's coordinates, and the length is the distance between the origin and the point.

Because coordinate values are number behaviors (NumberBvr objects), the direction and length of the vector behavior may change over time as the number behaviors change. For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to Vector2Bvr Objects
  • Statics Fields Relevant to Vector2Bvr Objects

Vector2Bvr Methods

div | Creates a new two-dimensional vector behavior that points in the same direction as the original vector but has a length that has been divided by scalar.
getPolarCoordAngle | Creates a number behavior that represents the counter-clockwise angular displacement, in radians, relative to the positive x-axis, of the vector behavior.
getPolarCoordLength | Creates a number behavior that is the length of the vector.
getX | Creates a number behavior that represents the x-coordinate of this vector behavior.
getY | Creates a number behavior that represents the y-coordinate of this vector behavior.
length | Creates a number behavior that represents the length of a vector behavior.
lengthSquared | Creates a number behavior that represents the length of a vector behavior squared.
mul | Creates a new two-dimensional vector behavior that points in the same direction as the original vector but has a length that has been multiplied by scalar.
normalize | Creates a normalized, two-dimensional vector behavior.
transform | Creates a new two-dimensional vector behavior that is the result of applying the given transformation to the original vector.
newUninitBvr | Enables you to refer to a Vector2Bvr behavior before that behavior has been defined.

div

Vector2Bvr Class

Creates a new two-dimensional vector behavior that points in the same direction as the original vector but has a length that has been divided by scalar.

Syntax

public Vector2Bvr div( NumberBvr scalar );

Parameters

scalar
NumberBvr object. This parameter can also be of type double.

Return Value

Returns the Vector2Bvr object.

See Also

mul

getPolarCoordAngle

Vector2Bvr Class

Creates a number behavior that represents the counter-clockwise angular displacement, in radians, relative to the positive x-axis, of the vector behavior. The range is [-Pi, +Pi].

Syntax

public NumberBvr getPolarCoordAngle( );

Return Value

Returns the NumberBvr object.

See Also

getPolarCoordLength, vector2Polar

getPolarCoordLength

Vector2Bvr Class

Creates a number behavior that is the length of the vector.

Syntax

public NumberBvr getPolarCoordLength( );

Return Value

Returns the NumberBvr object.

See Also

getPolarCoordAngle, vector2Polar

getX

Vector2Bvr Class

Creates a number behavior that represents the x-coordinate of this vector behavior.

Syntax

public NumberBvr getX( );

Return Value

Returns the NumberBvr object.

See Also

getY, vector2

getY

Vector2Bvr Class

Creates a number behavior that represents the y-coordinate of this vector behavior.

Syntax

public NumberBvr getY( );

Return Value

Returns the NumberBvr object.

See Also

getX, vector2

length

Vector2Bvr Class

Creates a number behavior that represents the length of a vector behavior.

Syntax

public NumberBvr length( );

Return Value

Returns the NumberBvr object.

See Also

lengthSquared

lengthSquared

Vector2Bvr Class

Creates a number behavior that represents the length of a vector behavior squared.

Syntax

public NumberBvr lengthSquared( );

Return Value

Returns the NumberBvr object.

Remarks

It is slightly more efficient to calculate lengthSquared than length.

See Also

length

mul

Vector2Bvr Class

Creates a new two-dimensional vector behavior that points in the same direction as the original vector but has a length that has been multiplied by scalar.

Syntax

public Vector2Bvr mul( NumberBvr scalar );

Parameters

scalar
NumberBvr object. This parameter can also be of type double.

Return Value

Returns the Vector2Bvr object.

See Also

div

normalize

Vector2Bvr Class

Creates a normalized, two-dimensional vector behavior. The normalized vector has the same direction as the original vector, but the length is 1.

Syntax

public Vector2Bvr normalize( );

Return Value

Returns the Vector2Bvr object.

transform

Vector2Bvr Class

Creates a new two-dimensional vector behavior that is the result of applying the given transformation to the original vector. The translation elements of the transformation are ignored; translation does not apply to vectors.

Syntax

public Vector2Bvr transform( Transform2Bvr xf );

Parameters

xf
Transform2Bvr object.

Return Value

Returns the Vector2Bvr object.

Remarks

This attribute composes values.

newUninitBvr

Vector2Bvr Class

Enables you to refer to a Vector2Bvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Vector2Bvr newUninitBvr( );

Return Value

Returns the Vector2Bvr object.


Vector3Bvr Class-Java Class

Vector3Bvr Class DirectAnimation Animated Header --Vector3Bvr Class * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Vector2Bvr
  * Next Topic: Viewer

Vector3Bvr Class

Creates an object that represents a three-dimensional vector behavior. At any given time, the value of the behavior is a direction and length (magnitude), specified as a triplet of coordinate values given as either Cartesian coordinates (x, y, z) or spherical coordinates (XYangle, YZangle, length). The direction of the vector is parallel to the ray that starts at the origin and passes through the point specified by the vector's coordinates, and the length is the distance between the origin and the point.

Because coordinate values are number behaviors (NumberBvr objects), the direction and length of the vector behavior may change over time as the number behaviors change. For more information about behaviors, see the Behavior class.

For relevant methods and fields from the Statics class, see the following topics.

  • Statics Methods Relevant to Vector3Bvr Objects
  • Statics Fields Relevant to Vector3Bvr Objects

Vector3Bvr Methods

div | Creates a new three-dimensional vector behavior that points in the same direction as the original vector but has a length that has been divided by scalar.
getSphericalCoordLength | Retrieves the NumberBvr object that is the distance of the vector from the origin.
getSphericalCoordXYAngle | Retrieves the NumberBvr object that is the counter-clockwise angle, in radians, of the vector about the y-axis, starting at positive z.
getSphericalCoordYZAngle | Retrieves the NumberBvr object that is the YZ angle, in radians, going up from the XZ-plane toward the positive y-ray.
getX | Creates a number behavior that represents the x-coordinate of this vector behavior.
getY | Creates a number behavior that represents the y-coordinate of this vector behavior.
getZ | Creates a number behavior that represents the z-coordinate of this vector behavior.
length | Creates a number behavior that represents the length of a vector behavior.
lengthSquared | Creates a number behavior that represents the length of a vector behavior squared.
mul | Creates a new three-dimensional vector behavior that points in the same direction as the original vector but has a length that has been multiplied by scalar.
normalize | Creates a normalized, three-dimensional vector behavior.
transform | Creates a new three-dimensional vector behavior that is the result of applying the given transformation to the original vector.
newUninitBvr | Enables you to refer to a Vector3Bvr behavior before that behavior has been defined.

div

Vector3Bvr Class

Creates a new three-dimensional vector behavior that points in the same direction as the original vector but has a length that has been divided by scalar.

Syntax

public Vector3Bvr div( NumberBvr scalar );

Parameters

scalar
NumberBvr object. This parameter can also be of type double.

Return Value

Returns the Vector3Bvr object.

See Also

mul

getSphericalCoordLength

Vector3Bvr Class

Retrieves the NumberBvr object that is the distance of the vector from the origin.

Syntax

public NumberBvr getSphericalCoordLength( );

Return Value

Returns the NumberBvr object.

See Also

getSphericalCoordXYAngle, getSphericalCoordYZAngle, vector3Spherical

getSphericalCoordXYAngle

Vector3Bvr Class

Retrieves the NumberBvr object that is the counter-clockwise angle, in radians, of the vector about the y-axis, starting at positive z. The range is [-Pi, +Pi].

Syntax

public NumberBvr getSphericalCoordXYAngle( );

Return Value

Returns the NumberBvr object.

See Also

getSphericalCoordYZ, getSphericalCoordLength, vector3Spherical

getSphericalCoordYZAngle

Vector3Bvr Class

Retrieves the NumberBvr object that is the YZ angle, in radians, going up from the XZ-plane towards the positive y-ray. The range is [-Pi/2, +Pi/2].

Syntax

public NumberBvr getSphericalCoordYZAngle( );

Return Value

Returns the NumberBvr object.

See Also

getSphericalCoordXYAngle, getSphericalCoordLength, vector3Spherical

getX

Vector3Bvr Class

Creates a number behavior that represents the x-coordinate of this vector behavior.

Syntax

public NumberBvr getX( );

Return Value

Returns the NumberBvr object.

See Also

getY, getZ, vector3

getY

Vector3Bvr Class

Creates a number behavior that represents the y-coordinate of this vector behavior.

Syntax

public NumberBvr getY( );

Return Value

Returns the NumberBvr object.

See Also

getX, getZ, vector3

getZ

Vector3Bvr Class

Creates a number behavior that represents the z-coordinate of this vector behavior.

Syntax

public NumberBvr getZ( );

Return Value

Returns the NumberBvr object.

See Also

getX, getY, vector3

length

Vector3Bvr Class

Creates a number behavior that represents the length of a vector behavior.

Syntax

public NumberBvr length( );

Return Value

Returns the NumberBvr object.

See Also

lengthSquared

lengthSquared

Vector3Bvr Class

Creates a number behavior that represents the length of a vector behavior squared.

Syntax

public NumberBvr lengthSquared( );

Return Value

Returns the NumberBvr object.

See Also

length

mul

Vector3Bvr Class

Creates a new three-dimensional vector behavior that points in the same direction as the original vector but has a length that has been multiplied by scalar.

Syntax

public Vector3Bvr mul( NumberBvr scalar );

Parameters

scalar
NumberBvr object. This parameter can also be of type double.

Return Value

Returns the Vector3Bvr object.

See Also

div

normalize

Vector3Bvr Class

Creates a normalized, three-dimensional vector behavior. The normalized vector has the same direction as the original vector, but the length is 1.

Syntax

public Vector3Bvr normalize( );

Return Value

Returns the Vector3Bvr object.

transform

Vector3Bvr Class

Creates a new three-dimensional vector behavior that is the result of applying the given transformation to the original vector. The translation elements of the transformation are ignored; translation does not apply to vectors.

Syntax

public Vector3Bvr transform( Transform3Bvr xf );

Parameters

xf
Transform3Bvr object.

Return Value

Returns the Vector3Bvr object.

Remarks

This attribute composes values.

newUninitBvr

Vector3Bvr Class

Enables you to refer to a Vector3Bvr behavior before that behavior has been defined. With this method you can create the behavior and use it in the definition of other behaviors, but not actually define its contents until some later point. (This is accomplished with the init method, which is available on all behaviors.) The system generates a run-time error if you initialize a non-uninitialized behavior, initialize an uninitialized behavior that has already been initialized, or run an initialized behavior that has not yet been initialized.

Syntax

public static Vector3Bvr newUninitBvr( );

Return Value

Returns the Vector3Bvr object.


Viewer Interface-Java Interface

Viewer Interface DirectAnimation Animated Header --Viewer Interface * Microsoft DirectAnimation SDK
  * Topic Contents

  * Previous Topic: Vector3Bvr
  * Next Topic: Java Defaults

Viewer Interface

The Viewer interface methods give complete control over a rendering device. Every method needed to view a model is included here. By implementing these abstract methods, it is possible to write custom viewers that can provide unique functionality. Developers can, for instance, control the tick rate of their models.

Viewer Methods

getCurrentTime | Retrieves the number of seconds since startModel was invoked.
getCurrentTickTime | Retrieves the time provided for the most recent call to tick or 0 if tick has not yet been called.
getPreferences | Enables the application to dynamically view the viewer preferences.
registerErrorAndWarningReceiver | Enables the application to register an object that will have its methods invoked upon asynchronous errors encountered when DirectAnimation media is controlling the frame loop or when recoverable errors occur when sampling and displaying the model.
startModel | Starts the behaviors associated with the model at global time 0.
stopModel | Forces all media to stop immediately.
tick() | Provides a shorthand way of writing tick(getCurrentTime()).
tick(timeToUse) | Explicitly performs a single sampling and display of the model, where timeToUse represents the global time.

getCurrentTime

Viewer Interface

Retrieves the number of seconds since startModel was invoked.

Syntax

public abstract double getCurrentTime( );

Return Value

Returns a double floating-point number.

getCurrentTickTime

Viewer Interface

Retrieves the time provided for the most recent call to tick or 0 if tick has not yet been called.

Syntax

public abstract double getCurrentTickTime( );

Return Value

Returns the double floating-point number representing the current tick time.

getPreferences

Viewer Interface

Enables the application to dynamically view the viewer preferences. For more information about preferences, see Preferences.

Syntax

public abstract Preferences getPreferences( );

Return Value

Returns the Preferences object.

registerErrorAndWarningReceiver

Viewer Interface

Enables the application to register an object that will have its methods invoked upon asynchronous errors encountered when Microsoft® DirectAnimation™ media is controlling the frame loop or when recoverable errors occur when sampling and displaying the model.

Syntax

public abstract ErrorAndWarningReceiver
     registerErrorAndWarningReceiver(
     ErrorAndWarningReceiver w
);

Parameters

w
ErrorAndWarningReceiver object to be registered.

Return Value

Returns the ErrorAndWarningReceiver object.

startModel

Viewer Interface

Starts the behaviors associated with the model at global time 0. Also causes the createModel method on the model itself to be invoked.

Syntax

public abstract void startModel( );

stopModel

Viewer Interface

Forces all media to stop immediately. It is not required to call this method, however, without it, some media (such as sounds) will continue playing until the object is released.

Syntax

public abstract void stopModel( );

tick()

Viewer Interface

Provides a shorthand way of writing tick(getCurrentTime()). It is the easiest way to explicitly sample and display model and provides a real-time clock for global time.

Syntax

public abstract void tick( );

tick(timeToUse)

Viewer Interface

Explicitly performs a single sampling and display of the model, where timeToUse represents the global time.

Syntax

public abstract void tick( double timeToUse );

Parameters

timeToUse
Global time for the single sampling.


Java Defaults-Java

Java Defaults DirectAnimation Animated Header --Java Defaults * Microsoft DirectAnimation SDK
    * Topic Contents

  * Previous Topic: Viewer
  * Next Topic: Java Event Data

Java Defaults

This is a list of defaults, by class. Defaults are system-defined.

Java Defaults by Class

Behavior.debug
The default of the boolean is true, which means the behavior is sampled when it is started. The default output device is the standard output device.
GeometryBvr.diffuseColor(ColorBvr col)
The default diffuse color is white.
GeometryBvr.lightAttenuation(NumberBvr const, NumberBvr linear, NumberBvr quadratic)
The default attenuation coefficients are (1, 0, 0). This means there is no light attenuation.
GeometryBvr.lightColor(ColorBvr col)
The default light color is white.
GeometryBvr.opacity(NumberBvr opac)
The default opacity is 1. This means complete opacity.
GeometryBvr.texture(ImageBvr image)
The default texture is no texture.
ImageBvr.opacity(NumberBvr opac)
The default opacity is 1.
LineStyleBvr.color(ColorBvr color)
The default color is black.
LineStyleBvr.dash(ds)
The default dash style is solid.
LineStyleBvr.detail()
The default detail style is on. This is the default LineStyleBvr and it has no width.
LineStyleBvr.end(EndStyleBvr es)
The default end style is flat.
LineStyleBvr.join(JoinStyleBvr js)
The default join style is beveled.
Path2Bvr.draw(LineStyleBvr ls)
The default line style behavior is defaultLineStyle.
Path2Bvr fill(ImageBvr im, LineStyleBvr ls)
The default line style behavior is defaultLineStyle.
MicrophoneBvr
The default microphone is located at the origin.
Preferences.COLOR_KEY_BLUE
The default is 245.
Preferences.COLOR_KEY_GREEN
The default is 254.
Preferences.COLOR_KEY_RED
The default is 1.
Preferences.DITHERING
The default is Enabled.
Preferences.ENGINE_OPTIMIZATIONS
The default is On.
Preferences.FILL_MODE
The default is FILL_MODE_SOLID.
Preferences.MAX_FRAMES_PER_SEC
The default is 30.
Preferences.OVERRIDE_APPLICATION_PREFERENCES
The default is Off.
Preferences.PERSPECTIVE_CORRECT
The default is On.
Preferences.RGB_LIGHTING_MODE
The default is On.
Preferences.SHADE_MODE
The default is SHADE_MODE_GOURAUD.
Preferences.TEXTURE_QUALITY
The default is TEXTURE_QUALITY_NEAREST.
Preferences.USE_3-D_HW
The default is Off.
Preferences.USE_VIDEOMEM
The default is On.
Statics.defaultFont
The default font is Times-Roman, black, 12pts in size, and neither bold nor italicized.
Statics.defaultLineStyle
The default is detail. This style has no width. It must be specified in meters.
Statics.public static Path2Bvr polyline(Point2Bvr[] points)
The default linestyle.


Java Event Data-Java

Java Event Data DirectAnimation Animated Header --Java Event Data * Microsoft DirectAnimation SDK
    * Topic Contents

  * Previous Topic: Java Defaults
  * Next Topic: Java Hierarchy Chart

Java Event Data

The following table summarizes the Microsoft® DirectAnimation™ events and the data they produce when the event occurs:
Event | Data
always | No data
andEvent(DXMEvent first, DXMEvent second) | A PairObject containing the event data of the first and second events
attachData(Object object) | The event data of the new event
GeometryBvr.getPickEvent() | A PairObject containing the intersection point of the mouse and the geometry, and a time-varying vector
ImageBvr.getPickEvent() | A PairObject containing the intersection point of the mouse and the image, and a time-varying vector
leftButtonDown | No data
leftButtonUp | No data
never | No data
notEvent(DXMEvent ev) | No data
notifyEvent(UntilNotifier notifier) | The results returned by the notifier
orEvent(DXMEvent first, DXMEvent second) | The event data of either the first or second event
predicate | No data
rightButtonDown | No data
rightButtonUp | No data
snapshotEvent | The value of the behavior at the time it was sampled, returned as a constant behavior.
thenEvent(DXMEvent e1, DXMEvent e2) | The time the second event fired and a PairObject containing event data for both events
timer(NumberBvr timeout) | No data
trigger() | No data
trigger(Behavior eventData) | The event data of the given event


Java Hierarchy Chart-Java

Java Hierarchy Chart DirectAnimation Animated Header --Java Hierarchy Chart * Microsoft DirectAnimation SDK
    * Topic Contents

  * Previous Topic: Java Event Data

Java Hierarchy Chart

The Microsoft® DirectAnimation™ Java Hierarchy chart shows the relationships among the DirectAnimation Java classes and interfaces and the standard Java library classes.


Copyright Notice

Information in this document is subject to change without notice. The names of companies, products, people, characters, and/or data mentioned herein are fictitious and are in no way intended to represent any real individual, company, product, or event, unless otherwise noted. Complying with all applicable copyright laws is the responsibility of the user. No part of this document may be reproduced or transmitted in any form or by any means, electronic or mechanical, for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

© 1999 Microsoft Corporation. All rights reserved.

Microsoft, MS-DOS, MS, Windows, Windows NT, ActiveX, BackOffice, DirectAnimation, DirectX, Developer Studio, J/Direct, J/Script, MSDN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual InterDev, Visual J++, WebTV, Win32, Windows, and Windows NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries.

Macintosh is a registered trademark of Apple Computer, Inc.

Other product and company names mentioned herein may be the trademarks of their respective owners.