荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Jobs (温少), 信区: Visual
标  题: Collaboration Data Objects: Using E-Mail in Your Application
发信站: BBS 荔园晨风站 (Mon Nov 29 19:19:52 1999), 站内信件



Collaboration Data Objects: Using E-Mail in Your Application
Dr. Bruce E. Krell and Ken Miller

January 1999

Summary: Discusses how to incorporate e-mail into your client applications
through the use of Collaboration Data Objects, a high-level set of COM objects
that allow easy access to e-mail systems embedded in Microsoft? Windows?
products. (14 printed pages)

Contents
Introduction
An Overview of CDO
A Few Important Implementation Details
Acquiring and Installing CDO
A Small Class for Easily Using CDO
An Application Test Driver
A Lot Of Gory Details
Summary

Introduction
Over several decades of software development, I have been asked to develop many
applications that incorporated e-mail. An example of this type of system
involves manufacturing control. Manufacturing control systems typically involve
several primary activities:

Monitoring manufacturing hardware


Evaluating the status of monitored hardware


Notifying interested engineers regarding status problems
Typically, problem notification consists of formulating and sending e-mail
messages to all addresses on a recipient list.

In order to accomplish these tasks, the following software architecture was
usually required.



The Status Monitor component has the responsibility for monitoring hardware.
Status data is collected. This data is then compared to an acceptable set of
ranges. If the data falls outside the required range, an e-mail message is
formulated. This message is then distributed to every address on a stored
recipient list. All messages are transmitted to the next component梐 Mail
Server. The Server holds these messages in a repository. When the Mail Client
component requests the messages, the Server transmits all messages to the
Client.

Now, in the olden days, I had to implement all these components myself. This
required a lot of coding and took some time, as you might guess. However,
because Microsoft Windows NT? now forms the basis of many control systems, I
can use a somewhat different architecture. You see, Windows NT has available a
powerful e-mail system that anyone can access from within their program. The
architecture I can use these days looks more like the following.



Yes, instead of writing my own messaging system, I can now use the facilities
of the Windows NT Exchange System for transmission on receipt of e-mail from
within my application. The Monitor simply submits the e-mail to the Microsoft
Outlook? client that resides on my local machine. If this client is connected
to the Exchange Server, the message is automatically forwarded to the Server.
If the client is not currently connected to the Server, the message is stored
in the local mailbox. Messages stay in the mailbox until a user explicitly
chooses to send the messages by executing the Send And Receive command provided
by the Outlook client.

Initially, you could access the e-mail services of Windows NT through the
Messaging API. I am sure that you have heard of this桵API. Unfortunately, the
MAPI approach was a little tedious, because you were expected to program all of
the low-level details for yourself. These days, however, your life is really
easy when using the native e-mail system. Now you have a Component Object Model
(COM) object called Collaboration Data Objects (CDO). As I will show you later,
you can easily incorporate e-mail into your applications, such as my Status
Monitor component, using the capabilities provided through CDO. Moreover,
access to CDO is really easy when you use CDO from within Java.

An Overview of CDO
Collaboration Data Objects are a high-level set of COM objects that allow you
to easily access the e-mail system embedded in the Microsoft Windows? product
line. CDO objects are generally used by client applications. For the most part,
CDO objects are used by clients wanting to access the e-mail system. Service
providers must be implemented using the native MAPI library.

The best mechanism for introducing you to CDO objects is to correlate these
objects to specific elements of the Outlook client user interface. Fundamental
CDO objects you utilize in your client application include the following.



These objects exist within a structure that you access within your client
program. The structure can be characterized as follows.



Higher-level objects within this structure contain the lower-level objects.
This containment relationship has a very important effect on you as a
programmer. In order to access an object at the lower level, you must access
all of the immediate upper levels. So, to get an input message, you must
execute the following sequence:

Get a Session object.


Get the Inbox Folder object from the Session object.


Get the Messages Collection object from the Inbox Folder object.


Get the Message object from the Messages Collection object.
Actually, all these gets are not really difficult to do inside Java. Access
methods provided by the class definition of each object convert this into a
simple method call. And, because you are working in Java, much of the low-level
details for working with a COM object are effectively hidden inside the Java
class implementation.

Note that a lot more objects exist than just these. You will see examples of
some of the other objects in the code listings later. Additional documentation
on the CDO objects and their usage can be found in the MSDN Library. Look under
the following topic sequence: Platform SDK/Database And Messaging
Services/Collaboration Data Objects.

A Few Important Implementation Details
Just for grins, let me show you the overall implementation architecture you are
using when you employ CDO. The layer of libraries you utilize is as follows.



You might think, with all this layering of COM objects, that you are incurring
a lot of overhead. Well, you are wrong. When you execute the sample code, you
see that the code executes quite rapidly. This performance occurs because
Microsoft has spent considerable time and effort to obtain high performance
from COM objects.

CDO COM objects are implemented as dispatch interfaces. This approach is used
to enable Microsoft Visual Basic? programs to access these objects. When you
pass arguments to any method supported by a dispatch interface, you must
package the argument in a special format. You may have seen this format before.
The format goes by the name Variant data type. Typically, any return values
come to your client application packaged into this data type. You must unpack
the data in order to get at the real data.

Variant data types are not really mystical entities. Basically, this type
allows you to represent a predefined set of data types in a format that is
independent of the programming language. You simply set one field indicating
the type of data, such as int. Then, the second field receives the actual int
data value. The COM object can tell from the first field how to access the
actual data.

Fortunately, this packing and unpacking is quite simple in Java. In keeping
with the Java approach to encapsulation, you have available a package that
provides a Variant class. The complete name for the class is
com.ms.com.Variant. Methods associated with this class allow for easy packing
and unpacking of data into and from a Variant format.

Let me show you a short example of packing a value into a Variant data type:

Declare a local variable or a field to hold a reference to the Variant object:
com.ms.com.Variant       ProfileName ;

Create a Variant object using the new operator:
ProfileName = new com.ms.com.Variant() ;

Stuff data into the Variant object using available access methods:
ProfileName.putString(Profile) ;

Note   Profile is a java.lang.String object initialized prior to this code.

Now, I know I told you earlier that Variant variables contain two fields.
However, in this code, you only set one value, Profile. In Java, the
encapsulation for the Variant type automatically sets the first field for you.
After all, because the access method indicates the data type of the original
data, setting the first field of the Variant variable is a mechanical process
that you can allow the computer to handle for you. (Hooray, an intelligently
handled abstraction!)

Different methods exist for inserting other data types, such as byte, int,
float, and double. A complete set of methods is also available for extracting
data, depending upon the stored data type. For more examples, see the following
code listings; or, check the documentation that installs with Microsoft Visual
J++?.

In fact, CDO objects are the elements of the CDO library that allow you to
manipulate lower-level data structures in the messaging system. With the CDO
library, you have direct access to all the data managed by the underlying
e-mail system. If you want to provide a visual interface to the e-mail system
from within your Web page, you must use another portion of the CDO library
called the rendering library. The rendering library is stored in a file named
cdohtml.dll. Using this library is outside the scope of this article.

Acquiring and Installing CDO
You can acquire CDO in one of several ways, for example:

Windows 95/98/NT Workstation:Download Outlook 98.


Windows NT Server:Download Exchange 5.5.
When you install Outlook 98, you should have cdo.dll, cdohtml.dll, and
mapi32.dll installed onto your system.

When you install Exchange 5.5, you must take special steps in order to install
these libraries. Choose Custom Setup. From this setup, select IIS/Active
Server/Active Server Pages Option. This setup process causes the earlier DLLs
to be installed on your system.

You may for some reason get stuck manually installing the CDO library. You can
easily do this with the following statement:    regsvr32   cdo.dll. Type this
command from a DOS prompt. The regsvr32 is a utility that invokes
self-registration routines embedded within an in-process COM object such as
cdo.dll.

Once you install CDO on your machine, you need to generate a Java wrapper class
for the CDO COM object. You can accomplish this by using the jactivex utility.
The command for creating a Java class wrapper is the following:

jactivex"  /javatlb  /xi  /d "destination folder" "c:\source folder\cdo.dll"

Yes, one or more spaces appears between /d and the start of the destination
folder name. If any of folder names contain spaces, the complete path
information must be surrounded by quotes.

An easier approach is to use the Visual J++ features for generating a COM
wrapper class. Start a new project. Choose Project from the main menu. Then,
select Add COM Wrapper from the pop-up submenu. These actions cause the
following dialog box to display.



Click the empty square next to Microsoft CDO library. Click OK. The result
jactivex is executed on the COM object DLL. A Java wrapper class is placed into
a subfolder within your project folder. This subfolder automatically appears in
your Project Explorer. You access the CDO classes through a new package named
cdo (all lower case梤emember, Java is case sensitive).

A Small Class for Easily Using CDO
Although CDO is much easier to use than direct MAPI, you still have a bit of
tedious programming to use CDO. In order to make CDO usage easier for you, I
have generated a small CDO wrapper class. A declaration for this wrapper class
is as follows:

class ConnectionClass
{
   private cdo.Session      CurrentSession ;
   private com.ms.com.Variant  NullParameter ;
      private cdo.Folder      InBox ;
   private cdo.Messages          InMessageCollection ;
       private cdo.Folder      OutBox ;
   private cdo.Messages          OutMessageCollection ;

   public ConnectionClass() ;

   public void ConnectToServer( String Profile ) ;

   public void SendMessageToServer(   String SenderName, String ReceiverName,
                                                                        String
SubjectText,  String MessageText) ;
   public int DetermineNumberOfMessagesFromSender(String SenderName) ;

   public void ReadMessageFromSender( String SenderName, int MessageNumber ,
                      StringBuffer  SubjectText,

StringBuffer  MessageText ) ;

   public void DeleteMessageFromSender(String SenderName, int MessageNumber)

   public void DisconnectFromServer() ;

       public void ReviewAllMessages(int InOrOut,String SearchName) ;
}

Notice the plethora of private fields declared at the start of the class. This
class maintains a single connection to the e-mail system. As you access the
e-mail system through various CDO objects, references to these objects are
automatically maintained for you in a meaningful way. Ah, the joys of using
encapsulation in Java. By using this class, you can save significant
development time when programming a client that interacts with the messaging
system. Methods in this class implement most of the functionality you will need
for basic messaging within your application. Of course, you can easily add
methods to this class to incorporate capabilities such as attaching files to
messages.

An Application Test Driver
First, let me show you how to use this class in a client application. As you
might guess, you must use the methods in a specific sequence to assure that
everything works correctly. This small driver program illustrates for you the
correct sequence of method calls.



Can you believe how readable this stuff is? Even your boss will be able to read
and understand what this code does. Writing readable code like this could
actually start a new trend梑osses and customers who believe in your work and
give you great raises.

Notice you do not see any of the details about the CDO objects being exposed to
the rest of the program. The private fields within this class maintain
references to the various CDO objects. Your application knows nothing about
these objects.

Hiding or encapsulating all this stuff provides added reliability to your
application. You debug the CDO details once inside this class. All other
applications or sections of the application use this class. Therefore, this
class becomes a building block for your application.

You can understand most of this code just by reading the code carefully,
because the methods look and read like English. However, a few of the arguments
to some of the methods need further explanation:

   Connection.ConnectToServer("Preferred Customer") ;

The argument to this method must be a profile that is registered with your
local Outlook client. If you use a name that is not registered, you are
prompted to create one. At this time, you cannot create a profile
programmatically using the CDO library. Programmatic creation of profiles is
apparently a server-side operation that is unavailable through CDO.

Connection.SendMessageToServer(   "70625.354@compuserve.com",

"swarch.krell@mci2000.com",
                  "Test Message",
                                                                        "This
Is A Test Message" ) ;

SenderName is the first argument to this method. This name must be a full
e-mail address. The second argument, ReceiverName, also must be a full e-mail
address. A String object defining the message subject is the third argument.
Finally, the message body comprises the fourth argument. This argument must
also be a String object.

A Lot Of Gory Details
I guess you would like to look at a few of these methods in detail. So, let's
get on with the job:

public ConnectionClass()
{
   CurrentSession = new cdo.Session() ;

   NullParameter = new com.ms.com.Variant() ;
   NullParameter.noParam() ;
}

This little gem is the constructor for my ConnectionClass. First, the
constructor creates a Session object. The Session object is defined in your COM
wrapper class named cdo. You may not realize this, but this usage of the new
operator is special. When you use new to create an instance of an object from a
wrapper class, the operator actually initializes COM for you and then creates
an instance of the COM object. In C++, these actions require several lines of
code with lots of arguments that you can just plain get wrong. Here, using Java,
 you let the Java Virtual Machine (JVM) do all the gritty work. After all, isn'
t this what computers are designed to do for you?

Next, the constructor creates a variant object named NullParameter. This object
is set to a system value indicating that no parameters are provided. You set
both fields of this object by invoking the method noParam:

public void ConnectToServer( String Profile )
{
   com.ms.com.Variant ProfileName ;

   ProfileName = new com.ms.com.Variant() ;
   ProfileName.putString(Profile) ;

   CurrentSession.Logon(ProfileName,
                                    NullParameter,NullParameter,NullParameter,
                                                NullParameter,NullParameter,
NullParameter) ;

   InBox = (cdo.Folder) CurrentSession.getInbox().getDispatch();
   InMessageCollection = (cdo.Messages) InBox.getMessages().getDispatch();

   OutBox = (cdo.Folder) CurrentSession.getOutbox().getDispatch();
   OutMessageCollection = (cdo.Messages) OutBox.getMessages().getDispatch();

}

Recall the hierarchy of contained objects. Well, the goal of this method is to
traverse through the hierarchy in order to cache references to the InBox, the
InMessageCollection, the OutBox, and the OutMessageCollection.

So, you initially stuff the profile name into another one of those pesky
Variant variables. You pass this to the Logon method provided by the Session
object. This method attempts to log you onto the Exchange Server residing on
the Windows NT Server that is registered for your client machine. This method
also connects you to the local Outlook client. In this way, you can transmit
messages to the local Outlook client if a server is unavailable. Also, you can
read and delete messages currently stored by the local Outlook client.

You then traverse both the input message and the output message object
hierarchies. First, you retrieve the InBox object from the Session object. Then,
 you request the InMessageCollection object from the InBox object. Both
references are cached into private fields in the class. A similar sequence
follows for the output message objects.

When you retrieve any of these objects using an access method such as getInbox,
you are really obtaining a reference to a Variant object. So, to get the real
Inbox object reference stored in this Variant, you need to call an access
method of the Variant object. Because all objects in CDO are COM objects
implemented as dispatch interfaces, you must extract a reference to a dispatch
interface from the Variant object. You extract the dispatch interface reference
through the getDispatch method. After all of this mishmash, you have a
reference to a dispatch interface. You can now invoke methods supplied by the
interface. So, when you invoke the getMessages method of the InBox object, you
are actually executing a method provided by the underlying dispatch interface.
However, unlike C++ programs, your program has a reference to the dispatch
interface, not a pointer to the dispatch interface. You are receiving all of
the protections of Java while using COM objects. Pretty neat, wouldn't you say?

public void ReadMessageFromSender(String SenderName, int MessageNumber ,
                                                              StringBuffer
SubjectText,
                                                              StringBuffer
MessageText )
{
   cdo.MessageFilter     SearchFilter ;
   cdo.Message        CurrentMessage ;

   com.ms.com.Variant FilterName ;
   com.ms.com.Variant Index ;
   com.ms.com.Variant Subject ;
   com.ms.com.Variant Text ;

   String   SubjectString ;
   String   TextString ;

   FilterName   = new com.ms.com.Variant() ;
   Index      = new com.ms.com.Variant() ;

   SearchFilter = (cdo.MessageFilter) InMessageCollection.getFilter()
.getDispatch();
   FilterName.putString(SenderName) ;
   SearchFilter. setSender(FilterName) ;

   Index.putInt(MessageNumber) ;
CurrentMessage = (cdo.Message) InMessageCollection.getItem(Index).getDispatch()
;

   Subject = CurrentMessage.getSubject() ;
   SubjectString = Subject.getString() ;
   SubjectText.append(SubjectString) ;

   Text = CurrentMessage.getText() ;
   TextString = Text.getString() ;
   MessageText.append(TextString) ;
}

In this method, you read a specific message from the list of messages from a
specific sender. These messages form a subset of the total list of messages in
the InMessageCollection.

The mechanism for targeting read operations on the InMessageCollection is to
use a MessageFilter object. In fact, the InMessageCollection object contains a
MessageFilter object. So, to limit all get operations to a specific sender, you
simply set the MessageFilter object properties accordingly. Setting the message
filter requires several steps:

Retrieve the filter, converting the returned Variant into a dispatch interface
reference.


Pack the sender name into a Variant named FilterName.


Set the Sender property of the filter using the Variant named FilterName.
In effect, these actions create a sublist containing only messages from the
indicated Sender. Now you can obtain one of these messages. The process is
admittedly a bit involved. You see, you obtain these messages by index number,
an integer starting with 1, not 0. But, of course, you are sending the index
number to a dispatch interface. So, before you can ask for a specific Message
object in the sublist, you pack the index number into a Variant, which is named
Index. The method that receives this Index Variant is the getItem method of the
InMessageCollection object. Because you previously set the MessageFilter, you
get a reference to a dispatch interface of the specific message in the targeted
sublist of messages (after extracting the reference from the returned Variant,
of course).

Now, you have a reference to a dispatch interface of a Message object. I guess
you would like to access data in the individual fields or properties, such as
Subject and Text. I bet you can even guess how to accomplish this. You call an
access method whose name begins with get. This method returns a reference to a
Variant. You then use a Variant method to extract the actual data. In the case
of a property that is a String object, you must create a StringBuffer object in
order to return the value to the caller of the method:

public void DisconnectFromServer()
{
   try
   {
      CurrentSession.Logoff() ;
   }
   catch( Exception e )
   {
      System.out.println("Logoff Failed") ;
   }
}

If you want to disconnect, you simply execute the Logoff method provided by the
Session object. Because this method throws an exception if you attempt to log
off from a Session object that you have not logged onto, you must catch the
exception. If you do not catch the exception, your client program simply does
not compile.

Notice that, throughout this code, you have created a number of COM objects
using the new operator. However, you do not have to do anything to release
these objects. Nor do you have to unload the COM system itself. The garbage
collector inside the JVM is pretty intelligent. When the garbage collector
recognizes that the COM objects are no longer needed, the JVM simply releases
the objects for you. And, if no longer necessary, the JVM also unloads the COM
system. Now, this type of garbage collection is really an intelligent approach
to the usage of Java and COM together.

Yep, a few more methods exist than these, and you really can read these methods
now, because you have a better understanding of how CDO works from inside Java.

Summary
As you read through the remainder of the code, remember the following concepts:

Primary objects used for accessing the e-mail system through CDO include:
Session


Folder


Messages Collection


Message
All CDO objects exist within a containment hierarchy.


Traverse higher-level contained objects to access lower-level objects.


All CDO objects are implemented as dispatch interfaces.


All inputs to methods must first be packed into Variant objects.


All outputs from methods must first be unpacked from Variant objects.


An overload of the new operator performs all COM initialization for you.


Intelligent garbage collection performs all COM termination for you.


Using a small class to further simplify access to CDO shortens development
schedule.


Using a small class to further simplify access to CDO leads to readable code.
With this introduction and the small class, you are ready to begin full-fledged
CDO implementations.

Dr. Bruce Krell (swarch.krell@mci2000.com) is Principal Architect for the
Software Architects, Inc. He has more than 30 years of development experience
in all types of target environments. His development areas of specialty include
high-performance image and signal processing applications and distributed
database applications. He has developed application-oriented courses in MFC
programming, SDK programming, Win32 system services, COM/ActiveX/ATL, device
drivers, and Java programming.

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

--
☆ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: bbs@192.168.11.111]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店