Figure 18-2 depicts the object model for CDOEXM. CDOEXM does not have many methods and properties, so the object model is easy to learn. The methods and properties make the most common administrative tasks easy to automate. To add a reference to CDOEXM in Microsoft Visual Basic, you just need to include the Microsoft CDO for Exchange Management Library (CDOEXM.DLL). You get this library wherever you install Exchange Server or the Exchange System Manager. The code can run either client-side or server-side as long as you have this object library on your machine.
CDOEXM and Active Directory Services Interfaces (ADSI) are tightly interwoven. You do not use CDOEXM to create Windows accounts in Active Directory ”you use ADSI. However, to mailbox-enable those Windows accounts, you can use CDOEXM. To access advanced properties in Active Directory, you also use ADSI. If you are going to use CDOEXM, you should be proficient with ADSI as well. Also, you should know the ADSI tools, such as ADSI Edit and LDP (which we discussed in Chapter 13), because they will help you investigate and configure Active Directory settings. One word of caution, though: do not try to set Exchange settings in Active Directory yourself for complex operations such as creating storage groups. Use CDOEXM to do the work because certain properties and processes must be implemented to ensure that the system does not become unstable or is not forced into an inconsistent state.
When you write code, you'll generally want to understand what kind of information about an Exchange server you can access programmatically. CDOEXM provides the ExchangeServer object, which has properties that return the version number, storage groups, and directory server that the Exchange server uses. Table 18-1 lists the properties of the ExchangeServer object. This object has no methods.
Property | Description |
---|---|
DataSource | Retrieves the IDataSource2 interface. From this interface, you can open a connection to an Exchange server by passing the name of the server to the Open method of the DataSource object. |
DaysBeforeLogFileRemoval | Specifies the number of days before Exchange Server log files will be removed from the server. Please refer to the Exchange Server documentation for the types of log files that Exchange Server uses. |
DirectoryServer | Returns the fully qualified domain name (FQDN) of the Active Directory domain controller that the Exchange server communicates with. This is a read-only property. |
ExchangeVersion | Returns the version of Exchange Server that is running. An example of output from this property is Version 6.5 (Build 6944) for the Release to Manufacturer (RTM) version of Exchange 2003. |
Fields | Returns the Fields collection. The fields you get back are mostly fields from Active Directory such as heuristics , whenCreated , whenChanged , and objectClass . |
MessageTrackingEnabled | A Boolean that specifies whether message tracking is enabled on the Exchange server. For more information on message tracking, refer to the Exchange Server documentation. |
Name | Returns the name of the Exchange server. This is a read-only property. |
ServerType | Returns a CDOEXMServerType enumeration. There are only two types: CDOEXMBackEnd and CDOEXMFrontEnd . Front-End (FE) and Back-End (BE) Exchange servers are discussed in detail in the Exchange documentation. |
StorageGroups | Returns an array that lists the Active Directory path to all storage groups on the server. You can then use these Active Directory paths with the StorageGroup object to open a specific storage group . |
SubjectLogging | A Boolean that specifies whether subject logging is enabled. |
The sample file named EMOSample.vbp, which you can find in the book's companion content, makes this clearer. Figure 18-3 shows the user interface for the sample.
Here is some of the code from the sample that uses the ExchangeServer object to determine server-specific information:
Dim oExchangeServer As New CDOEXM.ExchangeServer Dim arrSGs() As CDOEXM.StorageGroup Private Sub cmdConnect_Click() On Error GoTo errHandler: If txtExServerName.Text = "" Then MsgBox "You must enter a name in the Exchange Server name box!", _ vbCritical + vbOKOnly Exit Sub End If If Right(txtExServerName.Text, 7) = "LDAP://" Then 'Remove the LDAP txtExServerName.Text = Mid(txtExServerName.Text, 8) End If 'Try to connect oExchangeServer.DataSource.Open txtExServerName.Text 'Try to fill in Exchange Server info FillInExchangeServerInfo 'Retrieve the storage groups FillInStorageGroups Exit Sub errHandler: MsgBox "There was an error in cmdConnect_Click(). Error#" _ & Err.Number & " Description:" & Err.Description, _ vbCritical + vbOKOnly End End Sub Sub FillInExchangeServerInfo() On Error GoTo errHandler lblLogRemoval.Caption = oExchangeServer.DaysBeforeLogFileRemoval lblDirServer.Caption = oExchangeServer.DirectoryServer lblVersion.Caption = oExchangeServer.ExchangeVersion lblMsgTracking.Caption = oExchangeServer.MessageTrackingEnabled lblSubjectLogging.Caption = oExchangeServer.SubjectLoggingEnabled lblSGCount.Caption = UBound(oExchangeServer.StorageGroups) + 1 If oExchangeServer.ServerType = cdoexmBackEnd Then lblServerType.Caption = "Backend" Else lblServerType.Caption = "Frontend" End If Exit Sub errHandler: MsgBox "There was an error in FillInExchangeServerInfo. Error#" _ & Err.Number & " Description:" & Err.Description, _ vbCritical + vbOKOnly End Sub
The StorageGroup object allows you to work with storage groups. From this interface, you can retrieve the log file paths for the database, figure out how many databases and what types of databases are in the storage group, and create new storage groups. Table 18-2 lists the methods and properties of the StorageGroup object.
Method or Property | Description |
---|---|
CircularLogging | A Boolean property that specifies whether new log entries will overwrite the oldest log entries in the file. |
DataSource | Returns the IDataSource2 interface. From this interface, you can use the Open method to open a storage group by passing the Active Directory path to the storage group, or you can use the SaveTo method to create a new storage group. You can also use the Delete method to delete a storage group. |
Fields | Returns the ADO Fields collection for the storage group. Most of the interesting properties are already directly exposed by the StorageGroup object, so the Fields collection is not that interesting. |
LogFilePath | A string that specifies where the database transaction log files are stored. |
MailboxStoreDBs | An array of strings that contains the Active Directory path to any mailbox databases in the storage group. You can then use the Open method of the DataSource property on the MailboxStoreDB object to open the mailbox database. |
Name | Specifies the name of the storage group. |
PublicStoreDBs | An array of strings that contains the Active Directory path to any public folder databases in the storage group. You can then use the Open method of the DataSource property on the PublicStoreDB object to open the mailbox database. |
SystemFilePath | A string that specifies where database system files are stored. |
Zerodatabase | A Boolean that specifies whether deleted database pages will be overwritten with zeroes when you back up the server. |
MoveLogFiles | A method that takes a string that specifies the new path to move the transaction log files to. |
MoveSystemFiles | A method that takes a string that specifies the new path to move the database system files to. |
The StorageGroup object is straightforward. The following code shows how to use this interface. The following code from the EMOSample application opens a StorageGroup object and queries for the properties of the storage group.
Sub FillInStorageGroups() On Error GoTo errHandler 'Scroll through the array of storage groups and 'open each one into a SG object arrEXSG = oExchangeServer.StorageGroups Dim oTmpSG As CDOEXM.StorageGroup ReDim arrSGs(UBound(arrEXSG)) For i = LBound(arrEXSG) To UBound(arrEXSG) Set oTmpSG = CreateObject("CDOEXM.StorageGroup") oTmpSG.DataSource.Open "LDAP://" & arrEXSG(i) Set arrSGs(i) = oTmpSG 'Fill in the combo box comboSG.AddItem oTmpSG.Name Set oTmpSG = Nothing Next comboSG.ListIndex = 0 Exit Sub errHandler: MsgBox "There was an error in FillInStorageGroups. Error#" & Err.Number _ & " Description:" & Err.Description, vbCritical + vbOKOnly End Sub Sub FillInSGInfo(iIndex) lblCL.Caption = arrSGs(iIndex).CircularLogging lblLFPath.Caption = arrSGs(iIndex).LogFilePath lblMBDB.Caption = UBound(arrSGs(iIndex).MailboxStoreDBs) + 1 lblPFDB.Caption = UBound(arrSGs(iIndex).PublicStoreDBs) + 1 lblZeroDB.Caption = arrSGs(iIndex).ZeroDatabase lblSFPath.Caption = arrSGs(iIndex).SystemFilePath End Sub
To create a new storage group in your system, all you do is figure out the right Active Directory path to pass to the SaveTo method of the DataSource object on the StorageGroup object. This is not difficult, but the path will be long because the location where Exchange Server stores its information in Active Directory is buried a couple of layers deep in the hierarchy. Here's an example path to a storage group reference in Active Directory:
CN=First Storage Group,CN=InformationStore,CN=SERVERNAME,CN=Servers,CN=First Ad ministrative Group,CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=com
As you can see, storage groups are stored under Services, then Exchange, then your organization, then the administrative group, then the server, and finally the information store. Figure 18-4 shows this storage group object in ADSI Edit.
To create a new storage group, you just create a new StorageGroup object, generate the correct Active Directory path, and add a CN=MyStorageGroup name at the beginning. The following code creates a new storage group called MyNewSG using CDOEXM:
Dim oNewSG As New CDOEXM.StorageGroup oNewSG.DataSource.SaveTo "LDAP://server/CN=MyNewSG,CN=InformationStore," _ & "CN=SERVERNAME,CN=Servers,CN=First Administrative Group," _ & "CN=Administrative Groups,CN=First Organization,CN=Microsoft " _ & "Exchange, CN=Services,CN=Configuration,DC=DOMAIN, ,DC=com"
If you do not want to hardcode the path, you can just grab an existing storage group from the ExchangeServer object and parse it to get the path. The following code from the Training application does this:
'cut out the Storage Group name from the URL strTemp = oCDOEXMServer.StorageGroups(0) strTemp = Mid(strTemp, InStr(2, UCase(strTemp), "CN")) ' Build the URL to the StorageGroup strSGURL = "LDAP://" & oCDOEXMServer.DirectoryServer & "/CN=" _ & strFolderStoreName & "," & strTemp
A blank storage group is an interesting beginning to your application. You'll see later in the chapter how to create a new mailbox or public folder databases in a storage group. You'll also learn how to create a folder tree. Finally, you'll see how to create a new mailbox.
Deleting empty storage groups is no problem. You just open the storage group that you want to delete by using the Open method of the DataSource object on the StorageGroup object. If you find any databases in the storage group, delete them ”you cannot delete a storage group that contains any databases. Then call the Delete method of the DataSource object, as shown in the following code:
Dim oNewSG As New CDOEXM.StorageGroup oNewSG.DataSource.Open "LDAP://SERVER/CN=MyNewSG,CN=InformationStore," _ & "CN=SERVERNAME,CN=Servers,CN=First Administrative Group," _ & "CN=Administrative Groups,CN=First Organization," _ & "CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=com" oNewSG.DataSource.Delete
Before we talk about working with databases, let's briefly discuss how to work with public folders. If you don't plan to do anything with public folders, you can skip this section.
To create a new public folder database, you first create a new public folder tree that the database will be associated with. By having a separate tree, you can associate different databases with different public folder trees. This gives you the flexibility to build many different hierarchies of public folders for your applications. Mailbox databases do not have this requirement.
To work with folder trees, you use the FolderTree object. This interface has just three unique properties beyond the DataSource , Fields , and Name properties you saw earlier. These properties are listed in Table 18-3.
Property | Description |
---|---|
RootFolderURL | Specifies the URL to the root folder in the tree. Do not expect this to be the URL to use for your applications. For example, the public folders RootFolderURL value is http://SERVER/ExAdmin/ domain.com/Public%20Folders |
StoreDBs | A string array of Active Directory paths of other public store databases that contain replicas of this folder tree. |
TreeType | An enumeration that specifies the type of folder tree. The three possible values are cdoexmGeneralPurpose (a non-MAPI public folder tree), cdoexmMAPI , and cdoexmNNTPOnly . |
To create a new folder, you create a new FolderTree object. You set the name property on that object, and then you figure out the right Active Directory path to save your folder tree to, using the Save method of the FolderTree object's DataSource object. A typical path for a folder tree differs from that for a storage group. Here is an example path for the public folders folder tree. You would also use this path to open a folder tree.
LDAP://SERVER/ CN=Public Folders,CN=Folder Hierarchies,CN=First Administrative Group,CN=Admini strative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Conf iguration,DC=DOMAIN,DC=com
The following code creates a new folder tree called MyNewFT. Note that you must use the SaveTo method and save to the Folder Hierarchies container as well as add a CN=MyNewFT to the path.
Dim oNewSG As New CDOEXM.FolderTree oNewSG.Name = "MyNewFT" oNewSG.DataSource.SaveTo "LDAP://SERVER/CN=MyNewFT,CN=Folder Hierarchies," _ & "CN=First Administrative Group,CN=Administrative Groups," _ & "CN=First Organization,CN=Microsoft Exchange," _ & "CN=Services,CN=Configuration,DC=DOMAIN,DC=com"
To delete a folder tree, you open the folder tree and then call the Delete method on the DataSource object for the FolderTree object. You cannot orphan public folder databases, so you must first delete the database or disassociate the database with the folder tree before attempting to delete the folder tree. The following code deletes the folder tree we created earlier:
Dim oNewSG As New CDOEXM.FolderTree oNewSG.DataSource.Open "LDAP://SERVER/CN=MyNewFT,CN=Folder Hierarchies," _ & "CN=First Administrative Group,CN=Administrative Groups," _ & "CN=First Organization,CN=Microsoft Exchange," _ & "CN=Services,CN=Configuration,DC=DOMAIN,DC=com" oNewSG.DataSource.Delete
You can work with two types of databases in Exchange: mailbox databases and public folder databases. We'll look at public folder databases first. When you work with public folder databases, you use the PublicStoreDB object. Table 18-4 lists the properties and methods of this interface.
Element | Description |
---|---|
DataSource | Returns the IDataSource2 interface. From this interface, you can use the Open method to open a public folder database by passing the Active Directory path to the database or use the SaveTo method to create a new public folder database. You can also use the Delete method to delete the public folder database. |
DaysBeforeGarbageCollection | Returns or sets the number of days before deleted items in the database will be permanently deleted. See the Exchange Server documentation for more information on garbage collection. |
DaysBeforeItemExpiration | Returns or sets the number of days before items and folders will expire in the database. |
DBPath | A read-only property that returns the file path to the database file. This file will end with the .edb extension. |
Enabled | A Boolean property that specifies whether the database will be mounted at startup. A value of True means that the database will be mounted. |
Fields | Returns the ADO Fields collection for the object. |
FolderTree | A string property that returns or sets the Active Directory path to the folder tree associated with the public folder database. |
GarbageCollectOnlyAfterBackup | A Boolean that specifies whether items that have not been backed up will be permanently deleted from the database. A value of True means that items cannot be deleted until they are backed up. |
HardLimit | A property that specifies the size in kilobytes beyond which posting will be disabled. |
ItemSizeLimit | A property that specifies the maximum allowable size (in kilobytes) of an item in the database. |
Name | Specifies the name of the database. |
SLVPath | A read-only string property that returns the path to the streaming file database. The streaming file database contains data stored in its Internet content format. For more information about the database architecture of Exchange Server, see the product documentation. |
Status | A read-only property that returns a CDOEXMStoreDBStatus enumeration value that specifies whether the database is mounted. The enumeration contains four values: cdoexmOnline , cdoexmOffline , cdoexmMounting , and cdoexmDismounting . |
StoreQuota | A property that specifies or returns the maximum size (in kilobytes) of the database. |
Dismount | A method that dismounts the database. It takes an optional property that specifies the amount of time in seconds that the system will attempt to dismount the database. |
Mount | A method that mounts the database. It takes an optional property that specifies the amount of time in seconds that the system will attempt to mount the database. |
MoveDataFiles | A method that moves the data files for the database. It takes three parameters. The first is the new path for the database file. The second is the new path for the streaming files. The final parameter is optional and is a flags parameter reserved for future use. |
To create a new public folder database, you must have created your folder tree. You then create the Active Directory path to your public folder database. Next you create a PublicFolderDB object. Then you set the FolderTree property to the folder tree you created. Next you call the SaveTo method on the PublicFolderDB object with the Active Directory path. If you want to mount the database, you can then call the Mount method on the PublicFolderDB object. The following code shows how to create a new public folder database called MyNewPFDB. The folder tree MyNewFT must already exist.
Dim oPFDB As New CDOEXM.PublicStoreDB oPFDB.FolderTree = "LDAP://SERVER/CN=MyNewFT,CN=Folder Hierarchies," _ & "CN=First Administrative Group,CN=Administrative Groups," _ & "CN=First Organization,CN=Microsoft Exchange," _ & "CN=Services,CN=Configuration,DC=DOMAIN,DC=com" oPFDB.DataSource.SaveTo "LDAP://SERVER/CN=MyNewPFDB,CN=MyNewSG," _ & "CN=InformationStore,CN=SERVER,CN=Servers,CN=First Administrative " _ & "Group,CN=Administrative Groups,CN=First Organization," _ & "CN=Microsoft Exchange,CN=Services,CN=Configuration," _ & "DC=DOMAIN,DC=microsoft,DC=com" oPFDB.Mount
To delete a public folder database, you first open the database by passing the Active Directory path to the database, and then you call the Open method on the DataSource object for the PublicFolderDB object. Next you call the Delete method on the DataSource object. The following code deletes the MyNewPFDB database:
Dim oPFDB As New CDOEXM.PublicStoreDB oPFDB.DataSource.Open "LDAP://SERVER/CN=MyNewPFDB,CN=MyNewSG," _ & "CN=InformationStore,CN=SERVER,CN=Servers,CN=First Administrative " _ & "Group,CN=Administrative Groups,CN=First Organization," _ & "CN=Microsoft Exchange,CN=Services,CN=Configuration," _ & "DC=DOMAIN,DC=microsoft,DC=com" oPFDB.DataSource.Delete
When you create virtual directory entries in Internet Information Services (IIS) that point to Web files or folders that are stored in an Exchange database, IIS does not automatically add the proper Exchange ISAPI extension called Davex.dll to the virtual directory. This happens because you must create an Exchange virtual directory. If you just create a new virtual directory through Microsoft Internet Information Services (IIS), the virtual directory information will be stored in the IIS metabase, not in Active Directory. Exchange Server replicates information stored in Active Directory to the IIS metabase for creation of Exchange virtual directories. Therefore, the easiest way for you to create Exchange virtual directories programmatically is by creating them in Active Directory to begin with. Then you do not have to worry about creating the pointer to the Exchange Internet Server Application Programming Interface (ISAPI) yourself. If you want a graphical tool for creating Exchange virtual directories, use the Exchange System Manager.
Figure 18-5 shows the Exchange System Manager interface for creating a new Exchange virtual directory.
To programmatically create the entries in Active Directory using ADSI, you must get the container called 1 under the HTTP protocols section under the Microsoft Exchange section in Active Directory. Then you must create a new entry of class msExchProtocolCfgHTTPVirtualDirectory and set its properties appropriately. The following code from the Training application does this:
Function SetNewWeb(strComputerName, strFolderName, strDomainName,_ strVirDirName, bAddTrainingApp) On Error Resume Next Dim iServer As New CDOEXM.ExchangeServer Dim strFHname As String Dim NewWeb As IADsContainer Dim ExchFolder As Object Dim ADCont As IADsContainer Result = True 'Create an Exchange Virtual Directory so that OWA 'is the default view for the virtual directory iServer.DataSource.Open strComputerName Result = Result And GetFolderTreeURL(strComputerName, strFHname) WriteLog "Getting Object: LDAP://" & iServer.DirectoryServer _ & "/CN=1,CN=HTTP,CN=Protocols," & _ Mid(iServer.DataSource.SourceURL, _ InStr(1, iServer.DataSource.SourceURL, "CN=")) Set ADCont = GetObject("LDAP://" & iServer.DirectoryServer _ & "/CN=1,CN=HTTP,CN=Protocols," _ & Mid(iServer.DataSource.SourceURL, _ InStr(1, iServer.DataSource.SourceURL, "CN="))) Set NewWeb = ADCont.Create("msExchProtocolCfgHTTPVirtualDirectory", _ "CN=" & strVirDirName) NewWeb.Put "HTTPPubGAL", CBool(0) 'NewWeb.Put "anonymousAccount", "IUSR_MSEX2K" If bAddTrainingApp Then strFolderName = strFolderName & "\trainingapplication" End If NewWeb.Put "folderPathname", CStr(strFolderName) 'Allow Write access in Access Flags so user can create folders NewWeb.Put "msExchAccessFlags", CInt(535) NewWeb.Put "msExchAuthenticationFlags", CInt(6) NewWeb.Put "msExchBasicAuthenticationDomain", CStr(strDomainName) NewWeb.Put "msExchDefaultLogonDomain", CStr(strDomainName) NewWeb.Put "msExchDirBrowseFlags", -1073741794 NewWeb.Put "msExchLogonMethod", CInt(3) NewWeb.Put "msExchLogType", CInt(0) NewWeb.Put "msExchServerAutoStart", CBool(True) NewWeb.Put "msExchServerRole", CInt(0) NewWeb.Put "name", CStr(strVirDirName) NewWeb.SetInfo If Err <> 0 Then If Err.Number <> &H80071392 Then 'If virtual dir exists 'no need to raise an error MsgBox "Error creating virtual directory for " _ & strFolderName, vbInformation + vbOKOnly, App.Title WriteLog "Error creating virtual directory " & strVirDirName _ & " for folder tree " & strFolderName SetNewWeb = -1 End If Else SetNewWeb = 0 End If Set iServer = Nothing End Function
Table 18-5 lists the properties you must set (except those that are always the same value, such as msExchLogonMethod ).
Property | Description |
---|---|
anonymousAccount | Specifies the Windows account that IIS will use to authenticate users when they are accessing the Web site anonymously. |
folderPathname | Specifies the relative folder path. For example, a folder path to a folder called Helpdesk under Public Folders would be specified as Public Folders\Helpdesk . |
msExchAccessFlags | An integer that specifies the access flag for the virtual directory. You can combine the following values to get different levels of access:
For example, if you want read access to your virtual directory and you want to execute scripts, you can specify the value 513 for this property. |
msExchAuthenticationFlags | Specifies whether Anonymous, Basic, and/or Integrated Windows Authentication is allowed for the virtual directory. These are the values:
For example, if you want all authentication methods, you can pass 7 as the value for this property. Remember, though, that IIS restricts machine hops when you use NTLM authentication, and this can affect what security mode you use in an FE/BE configuration. You should use Basic Authentication over SSL in that scenario. |
msExchBasicAuthenticationDomain | If you're using Basic Authentication, you can specify the default Windows domain to try to validate the username and password against. If you do not specify this property, IIS will use the default domain on the server on which IIS is running. |
msExchDefaultLogonDomain | Specifies the default domain to try Exchange Server authentication against. This property is used when a client does not specify a domain to log onto in the browser logon box. |
msExchDirBrowseFlags | If you want to enable directory browsing, which allows users to look at the items in your folders pointed to by the IIS virtual directory, you must specify “1073741794 for this property. If you want to disallow directory browsing, you must specify 1073741854 . |
Any future changes you make to the virtual directory should be made through the Exchange System Manager and not through the IIS tools because those changes will not be replicated back into Active Directory. If you do make changes in the IIS tools and Exchange replicates the changes from Active Directory, the changes you make with the IIS tools might be overwritten and lost.
To work with a mailbox database, you use the MailboxStoreDB object. This object is similar in its properties to the PublicStoreDB object, and the methods are exactly the same. Table 18-6 lists the properties of this object that are different from those of the PublicStoreDB object.
Property | Description |
---|---|
OfflineAddressList | A string that specifies an Active Directory path to the offline address list for mailboxes stored in this database. |
OverQuotaLimit | Specifies the size limit (in kilobytes) for mailboxes in this database. If this limit is exceeded, the user cannot send mail. |
PublicStoreDB | A string that specifies the Active Directory path to the default public folder database for this mailbox. |
To create a mailbox database, you just create a MailboxStoreDB object, create an Active Directory path to the new database, and call the SaveTo method on the DataSource object for the MailboxStoreDB object. If you want, you can then call the Mount method to mount the new database. The following code creates a new mailbox database called MyNewMailboxDB :
Dim oMB As New CDOEXM.MailboxStoreDB oMB.DataSource.SaveTo "LDAP://SERVER/CN=MyNewMailboxDB," _ & "CN=First Storage Group,CN=InformationStore,CN=SERVER," _ & "CN=Servers,CN=First Administrative Group," _ & "CN=Administrative Groups,CN=First Organization," _ & "CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=com" oMB.Mount
Before you can delete a mailbox database, you must first be sure that there are no active mailboxes on that database. You can do this by moving all the mailboxes to a new mailbox database or by mail-disabling any of the users in the database. To delete the mailbox database, you just open the database using the Open method on the DataSource object for the MailboxStoreDB object, and then you call the Delete method.
Before you create a new mailbox, you should probably create a new user in Active Directory. The easiest way to create a new user programmatically is to use ADSI. Then you can create a new mailbox for that new user.
To create a new user using ADSI, you retrieve the container where you want to add the user. In the simplest case, this is the Users container. Then you create a new object in that container of the user class. You must set some properties, such as first name, last name, and account name. Then you call the SetInfo method to save the information into Active Directory.
The next step is to set the MailboxStore object you created to the IADsUser object you just created for the new user. This aggregates the MailboxStore object onto the IADsUser object. Then you can call the CreateMailbox method over the MailboxStore object. You pass the Active Directory path to the mailbox database where the new mailbox will be created. You then call the SetInfo method again to make sure the changes CDOEXM has made to the IADsUser object are saved back to Active Directory. The following code does all of this:
Dim objUser As IADsUser Dim objContainer As IADsContainer Dim objMailbox As CDOEXM.IMailboxStore Dim recipname As String, recip As String recip = "CN=New User" ' get the container Set objContainer = GetObject("LDAP://CN=users,DC=thomriznt5dom2," _ & "DC=extest,DC=microsoft,DC=com") ' create a recipient Set objUser = objContainer.Create("User", recip) objUser.Put "samAccountName", "newuser" objUser.Put "sn", "User" objUser.Put "givenName", "New" objUser.Put "userPrincipalName", "newuser" objUser.SetInfo objUser.SetPassword "password" objUser.AccountDisabled = False Set objMailbox = objUser 'Create a mailbox for the recipient 'You cannot create a mailbox using ADSI, so use CDOEXM objMailbox.CreateMailbox "LDAP://thomriznt52.thomriznt5dom2.extest." _ & "microsoft.com/CN=Mailbox Store (THOMRIZNT52)," _ & "CN=First Storage Group,CN=InformationStore,CN=THOMRIZNT52," _ & "CN=Servers,CN=First Administrative Group," _ & "CN=Administrative Groups,CN=First Organization," _ & "CN=Microsoft Exchange,CN=Services,CN=Configuration," _ & "DC=thomriznt5dom2,DC=extest,DC=microsoft,DC=com" objUser.SetInfo
Using the MailboxStore object, you can also delete and move mailboxes as well as set properties on the mailbox. Table 18-7 lists the properties and methods of the MailboxStore object.
Property | Description |
---|---|
DaysBeforeGarbageCollection | Specifies the number of days that deleted mail will be retained before it is permanently deleted. |
Delegates | Contains an array of string values that are the URLs of the other users who have access to this mailbox. |
EnableStoreDefaults | A Boolean that specifies whether to use the database defaults for storage limits. A value of True means that the default limits will be used. |
GarbageCollectOnlyAfterBackup | Specifies whether items can be permanently deleted only after the mailbox has been backed up. |
HardLimit | Specifies the size (in kilobytes) beyond which the user can no longer send and receive mail. |
HomeMDB | Specifies the Active Directory path to the mailbox store for the recipient. |
OverQuotaLimit | Specifies a size limit (in kilobytes) of a mailbox. If this limit is exceeded, sending mail is disabled. |
OverrideStoreGarbageCollection | Specifies whether this mailbox should use the garbage collection properties set on this mailbox or the properties set on the database. |
RecipientLimit | An integer that specifies the maximum number of recipients this user can send a single e-mail to. |
StoreQuota | Specifies the maximum size (in kilobytes) of the mailbox. |
CreateMailbox | Takes the path to the mailbox database where you will create a new mailbox. |
DeleteMailbox | Deletes the mailbox. |
MoveMailbox | Moves the mailbox to a new mailbox database. You must specify as a parameter the Active Directory path of the new mailbox database where you want to move the mailbox. |
To access a mailbox, you use both ADSI and the MailboxStore object. The following code opens a user in Active Directory and retrieves the MailboxStore - related object for that user:
Dim oMB As CDOEXM.IMailboxStore Dim objUser As ActiveDs.IADsUser Set objUser = GetObject("LDAP://CN=new user,CN=users," _ & "DC=thomriznt5dom2,DC=extest,DC=microsoft,DC=com") Set oMB = objUser
Before any object can become truly mailbox- or mail-enabled in the system, you must run a component of Exchange called the Recipient Update Service (RUS). The RUS populates a number of properties, such as the proxy address of the new user. The RUS can take seconds or hours to run, but you configure how often it runs through the Exchange System Manager. It runs asynchronously, so it will not fire on new mailbox creation. The following code sets the RUS to start processing immediately by setting the msExchReplicateNow attribute to true . Because the RUS is asynchronous, it will not run that very second, but this code can significantly reduce the time you have to wait for the RUS to run.
strDomainName = "domain" strDomain = "DC=domain,DC=com" strServer = "MyServer" 'Kick off the Recipient Update Service ' >>>> ToDo: Make sure the following string is correct by finding the RUS for ' your domain via ADSIEdit. strRUS = "CN=Recipient Update Service (" & strDomainName _ & "),CN=Recipient Update Services," _ & "CN=Address Lists Container,CN=Microsoft," _ & "CN=Microsoft Exchange,CN=Services," _ & "CN=Configuration," & strDomain set objRUS = GetObject("LDAP://" & strServer & "/" & strRUS) objRUS.Put "msExchReplicateNow", True objRUS.SetInfo
One of the most common programming tasks you will perform is to combine CDOEXM with CDO for Exchange (CDOEX). Some of the objects in CDOEX expose interfaces that you can use to retrieve certain CDOEXM interfaces. For example, the Person object in CDOEX also implements the IMailboxStore interface, so if the CDOEX Person object you are working with has a mailbox, you can retrieve the IMailboxStore interface for that person by using the GetInterface method on the Person object or by forcing CDOEX to do this for you by setting a MailboxStore object equal to a CDOEX Person object. An example is shown here:
Dim oPerson As CDO.Person Dim oMailbox As CDOEXM.IMailboxStore Set oPerson = CreateObject("CDO.Person") oPerson.DataSource.Open "mailto:thomriz@thomriznt5dom2.extest.microsoft.com" 'Set oMailbox = oPerson.GetInterface("IMailboxStore") 'Or you could coerce CDOEX to do it for you Set oMailbox = oPerson MsgBox oMailbox.RecipientLimit
To programmatically modify the permissions on your Exchange mailboxes, you can use the new MailboxRights property on the IExchangeMailbox interface in Exchange 2000 Service Pack 2 and later. This is a read/write property that can take an ADSI security descriptor. The following code uses this new property to set full control permissions for another user on a mailbox. You need the ADSI security DLL from the ADSI Resource Kit to run this code.
Dim SecurityDescriptor As IADsSecurityDescriptor Dim Dacl As IADsAccessControlList Dim newace As New AccessControlEntry Dim objUser As CDOEXM.IExchangeMailbox Dim objMailboxSD As IADsSecurityDescriptor Set objUser = GetObject("LDAP://domain.com/CN=users," _ & "CN=users,DC=domain,DC=com") Set objMailboxSD = objUser.MailboxRights Set Dacl = objMailboxSD.DiscretionaryAcl For Each obj In Dacl Debug.Print "Trustee:", obj.Trustee Debug.Print "AccessMask:", obj.AccessMask Debug.Print "AceFlags:", obj.AceFlags Debug.Print "AceType:", obj.AceType Next 'AceTypes: 'ADS_ACETYPE_ACCESS_DENIED = 1 'ADS_ACETYPE_ACCESS_ALLOWED = 0 newace.AceType = ADS_ACETYPE_ACCESS_ALLOWED 'Inheritance Flags 'CONTAINER_INHERIT_ACE = 2 newace.AceFlags = 2 'Set the AccessMask from ADSI 'ADS_RIGHT_DELETE = 0x10000, 'ADS_RIGHT_READ_CONTROL = 0x20000, 'ADS_RIGHT_WRITE_DAC = 0x40000, 'ADS_RIGHT_WRITE_OWNER = 0x80000, 'ADS_RIGHT_SYNCHRONIZE = 0x100000, 'ADS_RIGHT_ACCESS_SYSTEM_SECURITY = 0x1000000, 'ADS_RIGHT_GENERIC_READ = 0x80000000, 'ADS_RIGHT_GENERIC_WRITE = 0x40000000, 'ADS_RIGHT_GENERIC_EXECUTE = 0x20000000, 'ADS_RIGHT_GENERIC_ALL = 0x10000000, 'ADS_RIGHT_DS_CREATE_CHILD = 0x1, 'ADS_RIGHT_DS_DELETE_CHILD = 0x2, 'ADS_RIGHT_ACTRL_DS_LIST = 0x4, 'ADS_RIGHT_DS_SELF = 0x8, 'ADS_RIGHT_DS_READ_PROP = 0x10, 'ADS_RIGHT_DS_WRITE_PROP = 0x20, 'ADS_RIGHT_DS_DELETE_TREE = 0x40, 'ADS_RIGHT_DS_LIST_OBJECT = 0x80, 'ADS_RIGHT_DS_CONTROL_ACCESS = 0x100 'Exchange permissions correspond to Const ACE_MB_FULL_ACCESS = &h1 Const ACE_MB_DELETE_STORAGE = &h10000 Const ACE_MB_READ_PERMISSIONS = &h20000 Const ACE_MB_CHANGE_PERMISSIONS = &h40000 Const ACE_MB_TAKE_OWNERSHIP = &h80000 newace.AccessMask = 983041 'Set a valid user account newace.Trustee = "domain\user" 'Add the ACE to the DACL Dacl.AddAce newace 'Set changed DACL to Object objMailboxSD.DiscretionaryAcl = Dacl 'Set the MailboxRights property objUser.MailboxRights = Array(objMailboxSD) objUser.SetInfo