Exchange客户端之翼——WebDAV vs CDO(下)

        在上一篇文章里,我们已经提到了CDO,使用CDO编程发送邮件比较简单,但是CDO本身却是不简单的,先来看看什么是CDO以及包含哪些内容吧!

         CDO即Collaboration Data Objects的缩写,是一项可以用来建立用途广泛的协同作业应用程序的技术,能完成:接收、传送电子邮件,日程安排,联络人管理,工作流程以及exchange管理等。

         事实上,CDO是建立协作式应用程序的可靠API,我们知道ADO可以用来存取访问Web存储系统,但是它缺少协同作业最基本的功能,而CDO却可以处理一般的应用程序和高级的工作流应用程序解决方案。CDO甚至可以提供给我们管理exchange的能力,以及与windows 2000等AD(Active directory)的通讯(why,因为exchange与AD紧密集成嘛!^_^)。

        CDO并不是被封装成为一个单独的对象模块,相反它包含了3个不同的对象模块,每个都有其特定的用途,可以同时或单独使用这些模块,它们的DLL文件信息如下,你可以在Exchange的安装目录中找到它们。

        CDO For Exchange 2000 Server CDO.DLL

        CDO For Exchange Management   CDOEXM.DLL

        CDO Workflow For Exchange          CDOWF.DLL

         那么这三个对象模块的基本功能都有哪些呢,如下:

1. CDO for exchange 2000 server
最常用的CDO对象模块,该物件模块包括了一些协同作业应用程序中最常见的核心组件与接口。也可以使用该对象模块来建立与设定web存储系统中的文件夹,其中最上层的cdo对象常用来建立以下几种解决方案:
文件夹:(folder)在信箱存放区或公用存放区的容器。
人员:(person)WEB存储系统文件夹中的联络人类型的资料,或者是AD中的使用者或联络人对象。
邮件:(message)电子邮件
约会:(appointment)已经安排的约会或会议
日历信息(日程安排):(calendar message)会议邀请
收件者:(addressee)任何形式的电子邮件收件者或会议邀请的收件者
出席者:(attendee)包含于约会或者会议中的人员
所有最上层的对象都有两个重要的共同接口(interface)
Iconfiguration:用来定义可以应用于多个对象的行为模块,
Idatasource:每个CDO对象都公开Idatasource接口,用来管理、存放与存储资料。
2. CDO for exchange management objects
该对象模块是用来建立以及管理exchange信箱与exchange服务器组件的类别和接口所组成。当编写用来管理exchange收件者和信箱的程序代码时,会常用到。使用的对象是:
IMailRecipient:针对使用者指定电子邮件如何传送与管理,此对象也可以使联络人与文件夹不需要信箱便可以接收电子邮件。
IMailboxStore:指定如何建立、移动、删除exchange信箱,以及如何管理可使用信箱的收件者其可用来管理的属性。如果开发WEB架构的服务器管理工具,该对象模块提供让我们可以整体的管理服务器的对象,这些状况可用的最上层的对象有:
ExchangeServer:管理exchange2000服务器与传回基本信息,如服务器类型与版本。
FolderTree:管理服务其中的文件夹树状结构,包含任何副本。
StorageGroup:管理储存群组,用来组织信箱存放区和公用存放区。
MailboxStoreDB:管理单一使用者的信箱存放区可以使用此对象来连接、移动或中断连接信箱存放区,并传回有关存放区数据库的基本信息。
PublicStoreDB:建立、管理与删除公用存放区,并传回有关存放区数据库的基本信息。
3. CDO Workflow for Exchange
该对象模块是由用来建立以及执行工作流程与路由应用程序的类别和接口所组成。建立工作流应用程序有两种方式:
方式一:使用Workflow designer for Microsoft Exchange 2000 Server
方式二:使用自行建立工作流程事件流程与事件接受的方式。
我们使用前者,使用WorkFlow Designer我们就不需要大部分的工作流程对象,只有当编写回应各式各样工作流程动作的Script时,才需要使用这些对象,在我们的Script中最可能用到的对象如下:
IWorkflowSession:提供工作流程引擎、动作的Script与所处理的工作流程项目(ProcessInstance)之间的执行期(run-time)通讯。
IworkflowMessage:建立工作流程处理中要传送给收件者的通知邮件。
AuditTrailEventLog:于应用程序记录档(log)中,建立有关工作流程事件动态的项目(entries)。
   如果使用Workflow Designer无法提供完成要设计的功能,那就要自行建立事件接受,如果自行建立事件接受还需要熟悉下列对象:
ProcessDefinition:定义工作流程中的逻辑,包含状态与动作。

ProcessInstance:控制、记录与监视移动于工作流程处理之间、个别独立的工作流程项目的状态。

          了解了CDO的基本知识和组成要素后我们再来看一看

 一、如何使用CDO

1.   由类别建立对象
        CDO由多种COM的类别所组成,是用来建立新对象的基础,当一个对象被建立的时候,这些类别会决定对象标准的预设功能。如:CDO的message类别是用来定义新的Message对象,每个Message对象都有相同的标准功能,允许对象被传送与接受。
使用VB创建一个Message对象的例子:
dim msg as cdo.message
set msg=new cdo.message
使用Vbcript创建一个message对象:
dim msg
set msg=server.createobject(“cdo.message”)
这样我们就可以存取此类别预设接口中的属性与方法,以及任何该类别公开的其它接口。
使用完对象变量之后应该释放对象变量占有的内存:set msg=nothing
2.   使用介面(接口)
         界面定义了一个由属性和方法封装在一起的集合。
类别通常有多个与其相关联的介面,包含一个预设(default)的介面,此预设介面拥有类别既定的预设功能。预设介面与该对象呈现的名称相似,只是在对象名称前多了一个字母I,表示这是一个相对于类别对象的介面,如person类别有一个管理所有属性与方法的IPerson介面,特别用来定义与管理联络人类型的信息。这个预设的介面是由其所属对象直接公开,也就是我们可以由对象直接呼叫属性和方法。我们可以直接调用Person对象的e-mail属性,程序代码如下:
dim prs as cdo.person
set prs=new cdo.person
prs.email= someone@domain.com
除了默认介面以外,大部分的类别至少使用一个以上的其他介面,这些预设介面也会被其他类别所使用。参照介面以后才能够存取其属性和方法,可以轻易的经由调用相关属性来实现。如;Idatasource介面经由Datasource被存取,代码如下;(VB)
dim dsrc as Idatasource
set dsrc = prs.datasource
有些介面没有用以参照的相关属性,这时因该分情况采取不同措施,如果使用VB,则先宣告一个对象变量作为适当的介面,然后通过将介面对象变量指向对象公开的介面,自动完成参照介面的动作,如CDOEXM的IMailRecipient介面
VB:dim fld as cdo.folder
Dim rcp as cdoexm.imailrecipient
Set rcp=fld
Vbscript,不能为变量预先设定数据类型,必须使用getinterface方法存取介面,几乎每个CDO对象都公开用以存取介面的GetInterface方法,范例如下:

Set rcp = fld.getinterface(“imailrecipient”)

二、如果要了解CDO对象中的结构描述信息,可以通过其相关属性获得   

         ADO需要使用fields对象集合才能存取结构描述属性,而CDO提供一种更简单的方式,许多比较一般的结构描述属性都可经由CDO属性来存取。如:我们可以存取Person对象中的urn:schema:contacts:HomePhone结构描述属性,代码如下:
prs.HomePhone=”555-555-0101”
对结构描述属性而言并没有相等的CDO属性,必须使用adofields对象集合,它具有相同的属性与方法。要存取对象集合中的特定属性,就要传送完整并且合格的结构描述属性名称,若有相同的CDO相关常数(constant)也要一起传递,这些常数以cdo开头且包含于CDO函数库中,可以使用这些常数以省去传送完整属性名称的麻烦,使用CDO常数来传送时,其名称并不需要加上引号,如下例存取urn:schemas:contacts:FTPSite的属性:
prs.fields(cdoFtpSite)=”somearchive.edu”
如果要存取的结构描述属性没有可用的CDO属性或常数,就必须传递完整且合格的结构描述属性名称给Fields对象集合。下面的例子和上面的程序代码效果相同:
prs.fields(urn:schemas:contacts:ftpsite)=”somearchive.edu”
为了使程序更容易读懂,当程序中使用的属性不存在CDO属性或没有建立结构描述常数,我们应该为应用程序定义自己的常数。
三、使用URL存取Web存储系统中的资源
        
ADO 一样, CDO 也是依赖于 URL 以及 ExOLEDB provider 来存取 Web 存储系统中的资源,所要被存取的资源必须以完整的文件夹路径与显示名称( displayname )建构正确的 URL 地址,如果文件夹路径或项目的显示名称中有空格字符,那在 URL 中也必须保留名称中的任何空格字符。
CDO 也支持相对的 URL 用法 .
        CDO对象中一个很重要的接口
       
        IDataSource 接口是使用 CDO 存取 WEB 存储系统中资源的关键,所有的 CDO 对象经由 DataSource 属性公开 IDataSource接口 DataSource 属性可以用来在 WEB 存储系统中开启资源、侦测与储存资源的变更,以及建立新资源。
         
        使用 CDO 开启一个资源时,并非动态的连线到 WEB 存储系统,而是将资料由 WEB 存储系统中复制一个副本到本机的 CDO 对象,所以实质上您会有两份资料,一份再 WEB 存储系统中,另一份再 CDO 对象中,使用 CDO 对象的属性方法对资料所作的任何改变都是本机上的资料副本,而不是 WEB 存储系统中的资料,直到使用 IDataSource 的储存方法明确地指定要对资料作变更 WEB 存储系统中的资料才会被变更。如不调用一个储存方法,则变更的资料副本就会丢失。
        建立一个新的资源也是同样的道理。
四、如何使用CDO开启一项资源
       
        使用 CDO 对象中的 DataSource.Open 方法,它是以 record.open 方法为基础的开启资源的方法。 CDO 使用的 open 方法语法如下:
open ( source url as string [,activeconnection as object] [ mode as connectmodeenum 指定 record 如何被开启 ]) 默认以只读方式打开。
判断资料来源是否在本机被修改过: IDataSource 介面的 datasource.isdirty 属性 (boolean )
可以使用该属性判断本机的 CDO 对象是否被变更,如果变更,则使用 datasource.save 方法保存变更。
五、如何使用CDO建立新资源
     
       先为新资源建立一个 URL, 再将新资源的相关信息存储到该 URL,datasource.saveto 方法可以让您指定所建立资源的 URL,URL 必须是资源的精确的实际位置,还必须包含资源的 DAV:displayname.
Datasource.saveto 方法的语法:
Saveto(sourceurl as string [,activeconnevtion as object] [,mode as connectmodeenum] [,createoptions as recordcreateoptionsenum])
Mode, 指定 record 如何被开启
Createoptions, 指定资源如何被建立, adcreateoverwrite 将会覆盖已经在 URL 上存在的相同名称的资源项目 , 如果不设这个常数,则如果 URL 已经存在此资源的话,将会返回一个错误。
Datasource.savetocontainer 方法语法,该方法语法和 saveto 方法相同
 
       两者的区别: saveto 方法是具体知名资源项目的 URL DAV:displayname, 然后将资源项目存放到指定的 URL, 名称为指定的 DAV:displayname, savetocontainer 方法则是将资源存放到指定的 URL, 资源的 DAV:displayname exchange 自动产生的一个名称。
该方法的用途: savetrocontainer 方法在日程安排、建立约会时用得比较多,因为日程安排项目很多,时常有同名的约会名称,我们可能会将前面已经建立的越会覆盖掉,为了防止这样的事情发生,我们应该采用 savetocontainer 方法。
六、如何使用其它对象开启CDO对象
     
       可以使用 datasource.openobject 方法由其他记忆体中的对象打开 CDO 对象,而记忆体中的对象可以是其他 CDO 对象或是其他函数库的对象,比如 ADO2.5
openobject 方法的语法如下:
openobject(soure as Unknown, InterfaceName as String)
Source: 被要打开的对象所参照的已打开对象,例如一个 ado record 或一个已开启的电子邮件,通常是一个对象变量
InterfaceName:source 所公开的介面名称字串,例如,假使 source 是电子邮件的 bodypart 对象,则 InterfaceName 就是 BodyPart, 这个参数区分大小写。
为什么会使用该方法:
当我们已经使用 recordset 对象来浏览 WEB 存储系统的时候,可能使用 Connection 对象执行 SQL 查询来筛选所需要的项目,有或是批次的建立新项目,但是可能需要使用 CDO 来设定对象指定的属性,如果要修改使用 openobject 方法开启的 CDO 对象,需要使用 DataSource.savetoobject 方法将变更储存回 parent 对象,储存时传递 openobject 方法相同的参数给 savetoobject 方法。
检查文件夹或项目是否存在:
使用 ADO record open 方法,打开欲检查的 URL, 若成功打开,说明此 URL 已经有相同的资源,若不能打开,则说明资源或项目不存在。
七、如何使用CDO建立文件夹
    
       使用 CDO 建立新的文件夹的时候,要建立新的 CDO Folder 物件,然后设定您希望其运作方式的相关属性,假如知道你要设定文件夹保存特定形态的项目,便要设定 ContentClass 属性为其中一个事先定义的内容类别,若要文件夹也可以被 MAPI 的用户端所使用,则要设定 http://schemas.microsoft.com/exchange/outlookfolderclass 属性,假如有调用 fields 集合对象来设定属性,要先调用 update 方法,再使用 saveto 方法保存。
如果要删除一个文件夹或项目需要调用 record deletereocrd 方法。
八、如何使用文件夹接收电子邮件
     
当一个文件夹被建立时,预设并不能通过 SMTP 接受邮件,必须要明确地开启文件夹来接受电子邮件, CDO IMailRecipient 介面的 MailEnable 方法可以达到这个目的, exchange 会自动指定一个电子邮件地址给文件夹 .
如果在建立文件夹的同时就要开启文件夹接收电子邮件的功能的话,要先储存刚刚创建的文件夹,然后再调用 MailEnable 方法,如果使用已存在的文件夹,则不需要再将其存储一遍。
例程:
set fl=createobject(“cdo.folder”)
url=file://./backofficestorage/oaserver.exchange/
set rcp=fl.getinterface(“IMailRecipient”)
fl.datasource.save
set fl=nothing set rcp=nothing
计算文件夹中的内容:
使用 ADO 计算文件夹中的资源的时候,必须要浏览整个文件夹,这种方式忽视资源的内容是项目还是文件夹,而且当资源数量庞大时,会给服务器带来相当大的负荷
使用 CDO 可以避免程序中有不明确的状况发生,以及造成服务器负载过重, Folder 对象提供三种计算文件夹内容的属性:
ItemCount: 该属性计算文件夹中所有不包含子文件夹的项目的数量。
Unread ItemCount: 该属性只计算未被标记为已读取的项目数量。
VisableCount: 该属性只计算那些使用者可见并可存取的项目,忽略可能存储于文件夹中的系统隐藏项目。
       有关CDO编程发送邮件的例子在网上非常多,大家很容易就可以搜索到。这里要着重说明的是在客户端给某一联系人发送邮件之前还需要做一件事情,那就是必须先检查该联系人是否出现在AD的 users中,以及该联系人是否有对应的邮箱,从而确保邮件能够顺利到达。
       前面说过,Exchange与AD紧密集成,因此我们可以通过查找Exchange Server端的AD来检查联系人是否存在,然后再通过给该联系人发送空邮件检查其邮箱是否存在。示例代码如下:
       //add reference: System.DirectoryServices,MSXML2
       private bool ValidateMailboxAccount()
      {
             string pathAD = "LDAP://" + this.txtServer.Text.Trim(); //Exchange server name or IP
            DirectoryEntry myDE = new DirectoryEntry(pathAD, this.txtUid.Text.Trim(), this.txtPwd.Text.Trim()); // login username and password
            DirectorySearcher deSearch = new DirectorySearcher();
            deSearch.SearchRoot = myDE;
            deSearch.Filter = "(&(objectClass=user)(cn=" + this.txtAccount.Text.Trim() + "))";
            try
            {
                SearchResultCollection results = deSearch.FindAll();
                if (results.Count == 0)
                {
                    MessageBox.Show("The mail account is not existed!", "System Information", MessageBoxButtons.OK,
                        MessageBoxIcon.Warning);
                    this.txtUid.Text = "";
                    this.txtAccount.Text = "";
                    this.txtPwd.Text = "";
                    return false;
                }
                else
                {
                    HttpStatusCode myHSC = AuthorizationAccount(false, this.txtServer.Text.Trim(),
                        this.txtUid.Text.Trim(), this.txtPwd.Text.Trim());
                    if (myHSC == HttpStatusCode.NotFound)
                    {
                        MessageBox.Show("The appointed mailbox is not existed!", "System Information",
                            MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return false;
                    }
                }
            }
            return true;
      }
      private HttpStatusCode AuthorizationAccount(bool useSSL, string server, string uName, string passw)
        {
            MSXML2.IXMLHTTPRequest xmlHttp = new MSXML2.XMLHTTPClass();
            StringBuilder urlBuilder = new StringBuilder();
            string protocol = useSSL?"https://":"http://";
            urlBuilder
                .Append(protocol)
                .Append(server)
                .Append("/")
                .Append("Exchange")
                .Append("/")
                .Append(uName);
            xmlHttp.open("PUT", urlBuilder.ToString(),false,uName,passw);
            xmlHttp.send(null);
            return (HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), xmlHttp.status.ToString());
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值