Posts
47
Comments
55
Trackbacks
0
Sample Code: Calling Exchange cmdlets from .Net code

Now that CDOEXM and WMI providers are gone from Exchange 2007, many custom application developers are probably wondering: "what do I use instead?". The answer is the same as it is for admins who want to move from using CDOEXM/WMI to PowerShell, which is: use the Exchange cmdlets from code.

The PowerShell SDK provides a great set of APIs that will let you host an instance of the PowerShell engine and run cmds / scripts through it. So let's talk about how to code against PowerShell. Think of the PowerShell engine as being similar to the SQL engine. Both engines do specialized jobs, which require a dedicated processing engine. In SQL's case, the processing engine (the database) does all kinds of complicated stuff like joins, sorts, merges, relational queries etc. Well, to access all this good stuff, you need to invoke the database engine---basically you use SQL Command to interact with the engine. PowerShell is very similar: it has lots of specialized logic (pipelines, warning streams, progress streams, errors and so on) that can only be available through the PowerShell engine. Since all the Exchange cmdlets use this good stuff, you need to invoke the PowerShell engine when calling our cmdlets.

Enough talk, here is the sample app (very lightly tested, please use this is a template only).

Important Things to Note:  

You'll note that I don't have any references to Exchange namespaces, except adding the snapin. This is the recommended pattern, see "dealing with PSobject" below. You can also see in the sample that I don't treat the returned data as strong typed data. I deal with everything using "PSObject", which is a generic property bag that PowerShell keeps internally. This is again the recommended pattern. Another reason for dealing with MSHObjects and not casting to a strong type, is that Exchange has not interesting methods on the output classes. That's right---there are no methods on our output classes. The pattern is to use the property collection to find props and set/get them and to use cmdlets to achieve actions (like get/set/add/remove/mount/dismount etc.). The sample assumes an Exchange 2007 installation. Exchange 2007 beta 2 uses Monad RC0, whilst the SDK link above is the newly renamed PowerShell RC1, both are functionally equivalent, however that means that whereever you see PS in the name of a class or type, substitute MSH :)
Caveats:
You may see a 'path cannot be a null' exception when you pass in bad args to the app (and thus to the shell), this is a red herring, its our watson generating code messing up. Fixed in post beta 2 builds. Make sure to quote arguments properly when passing them to the app (and thus to the shell). For example: expshtest 'get-mailbox \"some user with space in their name\" '. Or expshtest 'get-mailboxdatabase \"server1\test db\" ' Make sure to pass in -confirm:$false to Exchange cmdlets that are destructive, or allow the user to pass in that option. Otherwise you'll get a 'host does not support operation' exception from PowerShell (actually right now you get the path cannot be null error. yuck!). This is a super simple app, so make sure to test for null args and so on. I stripped out all the basic stuff to keep the code small.

Sample: 

using System; using System.IO;
using System.Reflection; 
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Host;

namespace Test
{
 class TestOne
 {
  static void TestCallCmdlet(string args)
  {
   try
   {
           RunspaceConfiguration rc = RunspaceConfiguration.Create();            
          
MshSnapInException snapEx = null;

           MshSnapInInfo info = rc.AddMshSnapIn("Microsoft.Exchange.Management.Msh.Admin", out snapEx);

           Runspace r = RunspaceFactory.CreateRunspace(rc);
           r.Open();
           RunspaceInvoke ri = new RunspaceInvoke(r);
           ICollection results = ri.Invoke( args );
           List mailboxList = new List();

           foreach (MshObject mo in results)
           {
             MshPropertyInfo prop = (MshPropertyInfo)mo.Properties["Name"];
             if (prop != null)
                {
                    Console.WriteLine("Cmdlet: " + args + ",
val: " + prop.Value);
                    mailboxList.Add(prop.Value.ToString());
                }
           }
           ri.Dispose();
           r.Close();
   }
   catch (Exception ex)
   {
    Console.WriteLine(ex.Message.ToString() );
   }
  }

  static void Main(string[] args)
  {
   TestCallCmdlet(args[0]);
  }
 }
}

And here is the step to compile this sample (adjust it to match your install points):   

csc expshtest.cs /r:"c:\Program Files\Microsoft Command Shell\v1\System.Management.Automation.dll"

posted on Thursday, July 27, 2006 8:07 AM Print
Comments
Gravatar
# 
Bill Ashcraft
8/22/2006 6:08 AM
  
Can we see this in VB?
Gravatar
# 
sraddhan
11/1/2006 11:11 PM
  
can we remotely access to exchange server ?
Gravatar
# 
Steve
1/16/2008 7:01 PM
  
If you installed powershell
System.Management.Automation.dll Assembly is at
C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0
Gravatar
# 
Raffee
7/23/2007 3:07 AM
  
how can I get the System.Management.Automation dll?
Gravatar
# 
Márcio Martins
8/27/2007 10:08 AM
  
I used this approach to add a reference to System.Management.Automation:

- Extract System.Management.Automation.dll from the GAC running the command in a "cmd" window: copy C:\windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll C:\

Then opened the references window in VS2005 and browsed to the path which I extracted the dll.

It worked ;)
Gravatar
# 
Mark
10/16/2007 11:10 AM
  
How can we extract the value of a multi-valued property? e.g. In Exchange 2007 SP1, StandbyMachines is a MultiValuedProperty. It is a list of lists. We need to extract the value of NodeName from it.
Gravatar
# 
M.Laeeq
10/27/2007 7:10 AM
  
Can we add a reference to System.Management.Automation in VS2003? I tried it but failed to do so (an error occured). But when I added it to VS 2005, it was added successfully. Does it mean that PowerShell API are only accesible via VS 2005 or later.?
Gravatar
# Password problem
J. Tornell
3/11/2008 8:07 AM
  
Hi, how do you set a mailbox password through the invoke? Passwords in Exchange 2007 have to be securestring, as far as I can see, which you can´t combine with a string value.
Gravatar
# re: Sample Code: Calling Exchange cmdlets from .Net code
Vivek
8/19/2008 9:18 PM
  
You can create a secure string inside C#. Look at .Net securestring class, it has methods to append(). So what you can do is go through a regular string and append() it 1 by 1 to an empty securestring.

Post Comment

Title *
Name *
Email
Url
Comment *  
Please add 2 and 3 and type the answer here:
News
A little slow these days as I'm busy working on exchangelabs.com. I will try and post tidbits when I get some time. Enjoy the older posts till then!