August, 2008

  • Akash Blogging......

    HOW TO: Add SMTP ProxyAddresses to a mailbox using Exchange PowerShell and Managed code.

    • 3 Comments

    The key here is that you will first have to read all the proxy addresses using the Get-Mailbox Cmdlet and then call the Set-mailbox Cmdlet to update the ProxyAddresses. If this is not done you end up replacing all the existing addresses with the one you just added.

    To get the sample to work you will have to add a reference to the "System.Management.Automation.dll". This assembly is installed by the Windows PowerShell SDK and can be found at the following path \Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0

    Sample:

    using System;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Text;
    using System.Management.Automation;
    using System.Management.Automation.Host;
    using System.Management.Automation.Runspaces;
    
    namespace SetProxyAddresses
    {
        class Program
        {
            static void Main(string[] args)
            {
                 //To store the ProxyAddresses
                ArrayList alProxyAddresses = new ArrayList();
    
                RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
                PSSnapInException snapInException = null;
                PSSnapInInfo info = rsConfig.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapInException);
                Collection<PSObject> results;
                Runspace myRunSpace = RunspaceFactory.CreateRunspace(rsConfig);
                
                myRunSpace.Open();
    
                Pipeline pipeLine = myRunSpace.CreatePipeline();
    
                Command cmdGetMailbox = new Command("get-mailbox");
                cmdGetMailbox.Parameters.Add("identity", @"mycompany\VIP");
                pipeLine.Commands.Add(cmdGetMailbox);
                results = pipeLine.Invoke();
    
                if (pipeLine.Error != null && pipeLine.Error.Count > 0)
                {
                    foreach (object item in pipeLine.Error.ReadToEnd())
                    {
                        Console.WriteLine("Error: " + item.ToString() + "\n");
                    }
                }
    
                foreach (PSObject mailbox in results)
                {
                    //define the properties to get
                    foreach (String propName in new string[] {"EmailAddresses" })
                    {
                        Object objValue = mailbox.Properties[propName].Value;
                
                        if (objValue is ICollection)
                        {
                            ICollection collection = (ICollection)objValue;
                            // Loop through each entry in the collection and output it
                            foreach (object value in collection)
                            {
                                alProxyAddresses.Add(value.ToString());
                            }
                            PrintIndexAndValues(alProxyAddresses);
                        }
                    }
                }
    
                //Cleanup
                cmdGetMailbox = null;
                pipeLine.Dispose();
                pipeLine = null;
                            
                //add the additional address
                //Use SMTP as the prefix if you want to add and make it the Primary smtp address
                //Use smtp as the prefix if you just want to add a smtp address
                alProxyAddresses.Add("smtp:akashb@mycompany.com");
    
                //Create a new Pipeline
                pipeLine = myRunSpace.CreatePipeline();
    
                Command cmdSetMailbox = new Command("Set-Mailbox");
                cmdSetMailbox.Parameters.Add("Identity",@"mycompany\VIP");
                cmdSetMailbox.Parameters.Add("EmailAddressPolicyEnabled", false);
                cmdSetMailbox.Parameters.Add("EmailAddresses", (String[])alProxyAddresses.ToArray(typeof(string)));
                pipeLine.Commands.Add(cmdSetMailbox);
    
                results = pipeLine.Invoke();
    
                if (pipeLine.Error != null && pipeLine.Error.Count > 0)
                {
                    foreach (object item in pipeLine.Error.ReadToEnd())
                    {
                        Console.WriteLine("Error: " + item.ToString() + "\n");
                    }
                }
    
                //Cleanup
                cmdSetMailbox = null;   
                pipeLine.Dispose();
                myRunSpace.Close();
    
                pipeLine = null;
                myRunSpace = null;
            
            }
    
            public static void PrintIndexAndValues(ArrayList myList)
            {
                int i = 0;
                foreach (Object o in myList)
                    Console.WriteLine("ProxyAddress[{0}]:\t{1}", i++, o);
                Console.WriteLine();
            }
        }
    }

    You'll note that I don't have any references to Exchange namespaces, except adding the snapin and also that I don't treat the returned data as strong typed data. Everything is dealt with using "PSObject", which is a generic property bag that PowerShell keeps internally. This is the supported and recommended way to deal with this type of data. The pattern is to use the property collection to find props and set/get them and to use cmdlets to achieve actions like Get-Mailbox and Set-Mailbox.

  • Akash Blogging......

    Looping through Items in Outlook 2003 and having speed issues?

    • 1 Comments

    Looping through contacts or other items using Outlook Object Model and C++ is faster than using C#. The primary reason being the "Interop" layer and the marshalling of data that takes place when we use C# with Outlook Object Model.

    In this case I was looping through 10,000 contacts and it took me just 20-25 seconds in C++ and almost 2 minutes in C#. The challenge was to improve the performance. Here is how I did it.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Reflection; // to use Missing.Value
    using System.IO;
    using Outlook = Microsoft.Office.Interop.Outlook;
    
    namespace ReadContact
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                //Holds the start time
                DateTime start;
    
                //Holds the end time
                DateTime end;
    
                // Create the Outlook application.
                Outlook.Application oApp = new Outlook.Application();
    
                // Get the NameSpace information.
                Outlook.NameSpace oNS = oApp.GetNamespace("MAPI");
    
                // Log on by using a dialog box to choose the profile.
                oNS.Logon(Missing.Value, Missing.Value, true, true);
    
                // Get the contacts folder
                Outlook.MAPIFolder myContacts = oNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
    
                // Get the items in the contact folder
                Outlook.Items myContactItems = myContacts.Items;
    
                // Record the start time
                start = System.DateTime.Now;
    
                // use SetColumns - This greatly improves performance
                myContactItems.SetColumns("CompanyName,FirstName,LastName,Email1Address,IMAddress,BusinessTelephoneNumber, HomeTelephoneNumber,MobileTelephoneNumber");
    
                // Use the foreach loop and get each item as an object instead of Microsoft.Office.Interop.Outlook.ContactItem
                foreach (object item in myContactItems)
                {
                    //Use reflection to get the desired properties
                    string FirstName = (string)GetProperty(item, "FirstName");
                    string LastName = (string)GetProperty(item, "LastName");
                    string HomeTelephoneNumber = (string)GetProperty(item, "HomeTelephoneNumber");
                    string IMAddress = (string)GetProperty(item, "IMAddress");
                    string BusinessTelephoneNumber = (string)GetProperty(item, "BusinessTelephoneNumber");
                    string MobileTelephoneNumber = (string)GetProperty(item, "MobileTelephoneNumber");
                    string CompanyName = (string)GetProperty(item, "CompanyName");
                    string Email1Address = (string)GetProperty(item, "Email1Address");
    
                    Console.WriteLine("First Name:" + " " + FirstName);
                    Console.WriteLine("Last Name:" + " " + LastName);
                    Console.WriteLine("Home Telephone Number:" + " " + HomeTelephoneNumber);
                    Console.WriteLine("IM Address:" + " " + IMAddress);
                    Console.WriteLine("Business Telephone Number:" + " " + BusinessTelephoneNumber);
                    Console.WriteLine("Mobile Telephone Number:" + " " + MobileTelephoneNumber);
                    Console.WriteLine("Company Name:" + " " + CompanyName);
                    Console.WriteLine("Email Address:" + " " + Email1Address);
                }
                //Record the end time
                end = System.DateTime.Now;
    
                //Get the difference
                System.TimeSpan result2 = end.Subtract(start);
    
                //Print the time taken
                Console.WriteLine(String.Format("With SetColumns this took {0} ticks.", result2.Ticks));
                Console.WriteLine(start.ToString());
                Console.WriteLine(end.ToString());
    
                // Log off.
                oNS.Logoff();
    
                //Clean up
                oNS = null;
                myContacts = null;
                myContactItems = null;
                oApp = null;
            }
    
            private static object GetProperty(object targetObject,string propertyName)
            {
                return targetObject.GetType().InvokeMember(propertyName,
                  System.Reflection.BindingFlags.Public |
                  System.Reflection.BindingFlags.Instance |
                  System.Reflection.BindingFlags.GetProperty,
                  null,
                  targetObject,
                  null,
                  System.Globalization.CultureInfo.CurrentCulture);
            }
        }
    }

    Although I was not able to get the exact performance that I got using C++ but results were a lot better - from 2 minutes to 55-60 seconds.

  • Akash Blogging......

    How to force System.Net.Mail to use AUTH LOGIN instead of AUTH gssapi

    • 2 Comments

    When we send a mail out using System.Net.mail, It sends out a EHLO command, It then parses out the response to create a list of the Authentication mechanism that are supported by the SMTP Server.
    It then loops through the internal Authentication mechanisms which are listed below:
    -SmtpNegotiateAuthenticationModule
    -SmtpNTLMAuthenticationModule
    -SmtpDigestAuthenticationModule
    -SmtpLoginAuthenticationModule

    Then it compared each one against the list to check if it is supported by the SMTP server. If it is supported by the SMTP Server that Authentication mechanism is selected and it proceeds further. 

    System.Net.Mail code below(Visual Studio 2008 allows us to step through the source code):

    for (int i = 0; i < authenticationModules.Length; i++) 
     {
       //only authenticate if the auth protocol is supported  - [....]
       if (!AuthSupported(authenticationModules[i])) { 
          continue;
          } 
       NetworkCredential cred;
       cred=credentials.GetCredential(host,port,authenticationModules[i].AuthenticationType); 
    
       if (cred == null) 
                continue;
       Authorization auth;
       auth = SetContextAndTryAuthenticate(authenticationModules[i],cred, null);
       .............
     } 

    Assuming the SMTP Server supports SmtpNegotiateAuthenticationModule(gssapi) and SmtpLoginAuthenticationModule(Login) but we would want System.Net.Mail to use Login, How do we do it?

    We will have to build a class that implements ICredentialByHost, and then intercept calls checking for the methods we wish to block, and just return null for those.

    In the above code block when the call to "cred = credentials.GetCredential(host, port, authenticationModules[i].AuthenticationType);" is made, the code in our custom class is executed with the AuthenticationType sent as a parameter. If we return null, we hit the continue statement, System.Net.Mail switches to a different AuthenticationModule.

    Below is the class that we need to create and implement in our code:

    private class BlockGSSAPINTLMCredential : ICredentialsByHost
    {
      private NetworkCredential WrappedNetworkCredential;
      public BlockGSSAPINTLMCredential(string username, string password)
      {
          WrappedNetworkCredential = new NetworkCredential(username, password);            
      }
      #region ICredentialsByHost Members
      public NetworkCredential GetCredential(string host, int port, string authenticationType)
      {
        if (authenticationType.ToLower() == "gssapi" || authenticationType.ToLower() == "ntlm")
            {
                return null;
            }
        return WrappedNetworkCredential.GetCredential(host, port, authenticationType);
      }
      #endregion
    }

    In the code above we are returning null if the AuthentationType is “gssapi” or “ntlm” thus forcing System.Net.Mail to use “Login”.

    When sending out the mail, set the credentials in the code as below:

    client.Credentials = new BlockGSSAPINTLMCredential("user@domain.com", "Password"); 
  • Akash Blogging......

    HOW TO: Add ResourceDelegates for a Room Mailbox(s) based on a specified criteria using Powershell script

    • 0 Comments

    If you want to add ResourceDelegates for a Room Mailbox based on a specific criteria you can use the script below:

    In this case we add all users in a specific database as ResourceDelegates

    Create a .PS1 file as below:

    #This function returns all the users from a specific Database #This function can be altered to return mailboxes based on any other criteria

    Function GetUsers { param($DbName="") $UMailboxes="" $UMailboxes =Get-Mailbox -Database $DbName -RecipientTypeDetails Usermailbox return $UMailboxes } #Get the Room Mailboxes for which we have to set the Delegates #In this case we have to set the Delegates for the Room Mailbox tproom Get-Mailbox -identity tproom -RecipientTypeDetails RoomMailbox | foreach{ #Call the GetUsers function passing in the Database name. Set-MailboxCalendarSettings -Identity $_.Alias
    -ResourceDelegates (GetUsers "ex200701\First Storage Group\Mailbox Database") }


    To Run the script:
    1)Open the Exchange Management Shell
    2)Navigate to the location where you have stored the script file.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script.
  • Akash Blogging......

    Must read articles for VSTO 3.0 deployment for 2007 Microsoft Office System using Windows Installer

    • 0 Comments

    The following articles are a must read if you are deploying VSTO 3.0 solutions for 2007 Microsoft Office System using Windows Installer.

    Choosing Between ClickOnce and Windows Installer
    http://msdn.microsoft.com/en-us/library/ms973805.aspx

    Deploying a Visual Studio Tools for the Office System 3.0 Solution for the 2007 Microsoft Office System Using Windows Installer (Part 1 of 2)
    http://msdn.microsoft.com/en-us/library/cc563937.aspx

    Deploying a Visual Studio Tools for the Office System 3.0 Solution for the 2007 Microsoft Office System Using Windows Installer (Part 2 of 2)
    http://msdn.microsoft.com/en-us/library/cc616991.aspx

    Deploying Solutions for 2007 Office System with ClickOnce Using Visual Studio Tools for the Office System (3.0)
    http://msdn.microsoft.com/en-us/library/bb821233.aspx

  • Akash Blogging......

    HOW TO: Enable Mailboxes in different Storage Groups based on first character of First Name using Powershell script

    • 8 Comments

    In case you have already created the AD accounts and wants to enable mailbox and have a requirement that the mailboxes should be created in different Storage Groups based on the First character of the First Name, you can use the method shown below:

    In this case the Storage Groups are named as follows:
    A-B Users
    C-D Users
    E-F Users
    G-H Users
    ....
    The list of the users are available in a text file. Format of the text file is as below:

    The First row denotes the Field Names.The file is named UserAccounts.txt
    Account,First,Last
    rjohn,Robin,John

    Create a .PS1 file as below:

    #Gets the Server Name $server = [System.Environment]::MachineName #Domain Name $domain = "MyCompany" import-csv UserAccounts.txt | foreach{ #Create the ID $id = $("$domain"+"\"+$_.Account); $alias = $_.Account #Get the first character of the first name $str =$_.First.substring(0,1) $Database="" #Set the Database based on the first character of the first name #You might need to change the name of the Storage Group and Database based on
    #your
    environment. switch -regex ($str.ToLower()) { "[a-b]" {$Database = $server+"\A-B Users\"+"Mailbox Database"} "[c-d]" {$Database = $server+"\C-D Users\"+"Mailbox Database"} "[e-f]" {$Database = $server+"\E-F Users\"+"Mailbox Database"} "[g-h]" {$Database = $server+"\G-H Users\"+"Mailbox Database"} "[i-j]" {$Database = $server+"\I-J Users\"+"Mailbox Database"} "[k-l]" {$Database = $server+"\K-L Users\"+"Mailbox Database"} "[m-n]" {$Database = $server+"\M-N Users\"+"Mailbox Database"} "[o-p]" {$Database = $server+"\O-P Users\"+"Mailbox Database"} "[q-r]" {$Database = $server+"\Q-R Users\"+"Mailbox Database"} "[s-t]" {$Database = $server+"\S-T Users\"+"Mailbox Database"} "[u-v]" {$Database = $server+"\U-V Users\"+"Mailbox Database"} "[w-x]" {$Database = $server+"\W-X Users\"+"Mailbox Database"} "[y-z]" {$Database = $server+"\Y-Z Users\"+"Mailbox Database"} default {$Database = $server+"\First Storage Group\"+"Mailbox Database"} } # Run the actual command to enable the mailbox. enable-Mailbox -Identity $id -Database $Database -Alias $alias
    -DisplayName $($_.First+ " " + $_.Last) }

    To Run the script:
    1)Open the Exchange Management Shell
    2)Navigate to the location where you have stored the Data and the script file.
    3)Type in .\ScriptName.PS1 and hit enter to execute the script.

Page 1 of 1 (6 items)